Pascal Extensions and Enhancements

# Pascal Extensions and Enhancements

Alice supports a superset of ``standard'' Pascal (i.e. Pascal as described in The Pascal User Manual and Report by Kathleen Jensen and Niklaus Wirth). Much of Alice's extensions are based on WATCOM Pascal and are described in the WATCOM Pascal Tutorial and Reference Manual by F.D.Boswell, T.R.Grove, and J.W.Welch. However, to make our manual complete, this chapter will describe all deviations from standard Pascal, including those derived from WATCOM Pascal.

## The Program Statement

The contents of the program statement are entirely ignored. This means that the names of the program and of the files on the program statement may be the same as names in the body of the program.

## Names

The underscore ``_'' is a valid character in the middle of names, but may not be used as the first character. Thus ``input_line'' is a valid name, but ``_hello'' is not.

Alice Pascal is case insensitive -- the letters used to enter a symbol may be upper or lower case. It will recognize `NAME` , `name` , `Name` , `nAmE` , etc. as equivalent names. However, Alice always regards the declaration version of a name as the ``authoritative'' version. Thus if you enter `NAME` the first time you mention the name in a program, all subsequent references will be converted to this form. Even if you enter `name` , Alice will display it as `NAME` once the symbol is recognized.

## Strings

In standard Pascal, strings have the type

>     packed array [1..N] of char

where `N` is the length of the string. A strict interpretation of types would therefore prevent the assignment of a string constant to a string variable if the lengths are different. This makes life very difficult for people who want to write string manipulation subroutines.

To get around such problems, Alice Pascal supports the following extensions.

### String Constants

String constants can be enclosed in either single quotes or double quotes.

A double-quoted string of characters always has the type packed array of `char` , regardless of how many characters are inside the quotes. This means that you may have null strings (`""` , zero characters), and single character strings (e.g. `a` ). According to the strict definition of Pascal, a single character in single quotes always has the type `char` instead of packed array of `char` . Thus you could not assign `'a'` to a packed array of `char` variable but you could assign `"a"` .

### String Compatibility

In Alice Pascal, all string types are assignment compatible. By a string type, we mean anything with a type of the form

>     packed array [1..N] of char

Furthermore, all string constants are assignment compatible to string types.

If you assign a longer string to a shorter string, the assigned string will be truncated to the appropriate length. For example, suppose `str` is a string type with length 3.

>     str := "abcde";

stores the string `"abc"` in `str` .

If you assign a shorter string to a longer string, the characters of the shorter string are placed in the longer string and then a special ``end of string'' marker named `StrEnd` is added to show the end of the string. For example, suppose `str6` is a string type variable of length 6. If we assign

>     str6 := "lgs";

`str6` will have the following elements.

>     str6[1] = 'l'
>     str6[2] = 'g'
>     str6[3] = 's'
>     str6[4] = StrEnd
>     str6[5] is undefined
>     str6[6] is undefined

The `StrEnd` character has the type `char` . It is the ASCII null, so its ordinal value `ord(StrEnd)` is zero. If we assign

>     str6 := "abcdef";

there is no `StrEnd` character because all six elements of `str6` are filled by characters in the string.

Strings of different lengths are comparison compatible as well as being assignment compatible. For example, one might say

>     if str1 > str2 ...

Comparisons are made character by character using the ASCII collating sequence. Comparisons will stop at the end of the shorter string or when `StrEnd` is encountered in either string.

String constants are compatible with the `Pointer` type. This is discussed in the explanation of the `Pointer` type, later in this chapter.

### String I/O

If a string type variable is specified in a `read` or `readln` instruction, Alice will place one character at a time into the variable until the variable is full or end-of-line occurs. If the reading stops because of end-of-line, a `StrEnd` marker will be placed in the string after the last character read.

## Declarations

The following extensions are related to declarations.

### Multiple Declaration Blocks

Alice Pascal lets you have several blocks of the same kinds of declaration in a single subprogram or the program mainline. For example, you can have several separate var blocks if you wish. This can happen if you bring in a lot of declaration blocks with the MERGE command or if you type a declaration keyword like var on a `Declarations` placeholder. The basic provision is that you may not have forward references (except when declaring a pointer type to a type that has not yet been declared).

### Forward Declarations

Alice does not work with forward declarations in the same way that standard Pascal does. See Chapter 5 for more details.

### Passing Subprograms as Parameters

Alice lets you pass functions and procedures as arguments to other subprograms. To define a subprogram that takes such an argument, enter the keyword function or procedure in an empty `Parameter` placeholder in the heading of the subprogram. Alice will lay out a formal function or procedure parameter declaration. The formal declaration requires a full list of the parameters that the function or procedure takes. Thus you might see

>     procedure findroot(function f(x:real):real);

at the beginning of a procedure definition. The `findroot` procedure takes one argument. This argument happens to be a function. Inside the code of `findroot` , the argument function will be called `f` . The argument function takes one `real` argument and returns a `real` value.

You must type in a function or procedure keyword for every formal parameter. This means that you can't say something like

>     function f(procedure a,b) ...

Instead, you have to use

>     function f(procedure a; procedure b) ...

### Set Types

The base type of a set may contain a maximum of 256 elements. Moreover, the maximum ordinal value (from the `ord` function) in the base type must be in the range 0..255. None of the elements of the base type can be negative. This means that `set of char` is all right, but `set of integer` is invalid, as well as sets of subranges like 0..1000 (upper limit is too high) and -2..2 (can't have negatives). You can create a set of any enumerated type.

### Multi-Dimensional Arrays

Alice supports multi-dimensional arrays but uses a syntax that is slightly more restricted than standard Pascal.

Pascal permits declarations like

>     array [1..10,1..20] of char;

Alice does not allow multiple ranges inside a single set of braces. If you enter `1..10` followed by a comma inside the braces, Alice will automatically expand this to

>     array [1..10] of array [Range] of Type;

You can then fill `1..20` into the second `Range` placeholder.

Similarly, Alice will automatically convert subscripting operations like

>     arr[M,N]

into

>     arr[M][N]

Note that you may still enter your subscripts using the first format (and APIN can translate the format properly). Alice will just display the subscripts using the second format.

## Statements

The following extensions are related to statements.

### ELSE Clause in CASE Statements

case statements may have an else clause. This clause is executed if none of the other case instances match the value of the case selection expression. For example, you might have

>     case power of
>       1: begin
>           writeln(x);
>           end;
>       2: begin
>           writeln(x*x);
>           end;
>       3: begin
>           writeln(x*x*x);
>       else begin
>           writeln("power has unacceptable value");
>           end;
>       end;

Unlike the other case labels, there is no colon after the else. To get an else clause in Alice, just enter the keyword else on a `Case-Instance` placeholder.

If a case statement has two else clauses, you will be given an error message, but not until run-time.

### WITH Statements

Alice does not support multiple record variables in a single with statement. Standard Pascal accepts

>     with a, b do ...

but Alice requires the equivalent form

>     with a do begin
>        with b do begin ...

### GOTO Statements

You may not use a goto to jump into the middle of a begin block from outside the block. This applies to all begin blocks, including the ones inside other structures (e.g. for or while statements).

You can use goto to jump out of such a block, provided that you are not using EXECUTE or superstepping at the time.

## Calling Subprograms

As in regular Pascal, you may not define a function that returns a structured type (i.e. an array, a record, a set, or a file).

If you are passing a record or array as a value argument, it is not necessary to initialize every field in the record or every element in the array. You will, however, receive an error if the called routine tries to use an uninitialized record field or array element.

A subprogram may declare parameters with subrange types. However, Alice will not check that arguments passed to the subprogram fall inside the declared subrange -- Alice only checks that the arguments have the proper base type.

## Debugging Features

Pressing the `[CTRL-BREAK]` key will stop an executing program. The program will be placed in a suspended state, allowing you to examine it using debugging facilities like immediate mode. Execution can usually be continued via the CONTINUE or STEP commands (see Chapter 4).

Alice performs a number of error-checks that are not usually found in other versions of Pascal.

1. At run-time, Alice detects the use of uninitialized variables, i.e. variables that have not yet been assigned a value.
2. Alice checks subrange bounds in scalar assignments.
3. Alice detects array subscripts that have gone out of bounds.
4. Alice checks for argument values that are out of bounds when calling built-in routines.
5. Alice also detects the following:

-- indirecting through a `nil` pointer

-- set elements out of bounds

-- division by zero

-- modulus values out of bounds

-- case values that match no labels

-- `succ` or `prec` out of bounds

-- goto an invalid label

-- attempts to read beyond end-of-file

and a number of other erroneous conditions.

On the IBM PC, Alice does not detect arithmetic overflow or underflow yet.

## Standard Library Routines

The following extensions are related to standard library routines. Non-standard library routines are described in separate sections later in this chapter.

### The readln Procedure

`readln` takes an optional string constant that will be used as a prompt for input. For example,

>     readln("Enter a value for x: ",x);

would print out

>     Enter a value for x:

and then wait for the user to enter an appropriate value.

### Terminal Input for Read and Readln

Input is only read from the terminal when the program calls for it, and then Alice reads an entire line. This is slightly different than standard Pascal, which is always one character ahead of the user. When you are entering an input line, you may correct typing mistakes with backspacing. Nothing from the input line is passed to the program until you press `[ENTER]`.

Note that calls to the `eoln` and `eof` functions actually perform a read operation on the terminal to see if there is a character waiting to be read. This can cause some problems. For example, suppose you have

>     while not eof do begin
>         writeln("Enter integer");
>         readln(i);
>         ...
>         end;

The call to `eof` causes Alice to read an entire line of input from the terminal. This happens before the `writeln` instruction writes out the prompt for input. You may have to rewrite programs slightly to get the prompt in the right place.

You can create an explicit ``end-of-file'' condition by pressing F6 as the first character of an input line. You do not have to press `[ENTER]`.

If you ask `read` or `readln` to read a number from the terminal, but they find something non-numeric, they throw away the rest of the input line and keep looking for a number. For example, suppose you say

>     read(i,j,k);

and enter the input line

>     15 b 68

the variable `i` gets the 15, then a warning is printed indicating that Alice expected numeric input but found a `b` . Alice will read in another line of input to give values to `j` and `k` .

### Reset and Rewrite

The standard procedures `reset` and `rewrite` have been extended. They now have the form

>     reset(filevar,filename);
>     rewrite(filevar,filename);

In both cases, `filevar` is a normal file variable. The `filename` argument is a string giving the name of the file that should be associated with the file variable. For example,

>     reset(indata,'myfile');

associates the file named ``myfile'' with the file variable `indata` and opens ``myfile'' for input.

If the file name argument is omitted, Alice will use the name of the file variable as the file name. For example, if you say

>     rewrite(info);

Alice will write output to a file named ``info''. If the file variable is a field in a record, Alice will use the name of the record variable, not the field name.

The special file format

>     ?:file

may be used in `reset` , `rewrite` , and `append` calls, as in

>     reset(filevar,"?:mydata");

As noted in Chapter 5, this format tells Alice to search a number of devices for the desired file.

Files are closed automatically upon completion of the subprogram in which they are opened. If they are opened in the program mainline, they are closed upon program termination.

### Writeln

If the user doesn't specify a field width or a precision when writing out a real number, `writeln` chooses an ``optimal'' format for the output. This may or may not be exponential format (standard Pascal always uses exponential format). If a field width is specified but not a precision, Alice will use exponential notation. If both a field width and a precision are specified, Alice will write the number in the format requested.

If no field width is specified when writing an integer, Alice will print the integer with a single leading blank. If you do not want this leading blank, specify a field width of zero.

You may use `writeln` to write enumerated type values. Unlike many other versions of Pascal, Alice will write out the actual name of the value. For example, if you have

>     type
>         colour = (red,blue,green);
>         ...
>     writeln(red);

Alice will write out the word `red` .

Similarly, Alice will write out sets as a list of values surrounded by square brackets. You may thus use `writeln` to write out sets.

### The new Procedure

If `new` is being used to allocate space for a record that has a variant part, standard Pascal lets you specify values for the tag field(s) of the variant part. By plugging in the tag field value(s), the `new` procedure can calculate the minimal amount of memory required to store the new record.

In the current implementation of Alice, tag field arguments can be passed to `new` , but the procedure does not use them. It always allocates enough space to store the maximum possible length of the record.

### Unimplemented Routines

Several standard subprograms have not been implemented in the current version of Alice. Alice recognizes their names and programs can contain calls to these routines, but the routines do nothing. The unimplemented routines are

>     pack
>     unpack

In addition, the `dispose` routine only does half of what it's supposed to -- it deletes something allocated with `new` but does not make that memory available for a new use.

## New Built-In Symbols

The built-in subprograms, types, etc. discussed in this section are not found in ``standard'' Pascal.

### String Manipulation Routines

String manipulation routines manipulate string constants and variables with string types. Our descriptions of these routines will give examples of source code using each routine, followed by output from this source code.

#### `StrConcat(str1,str2);`

This procedure concatenates the contents of `str2` on the end of `str1` .

>     str1 := "hello";
>     str2 := " there";
>     StrConcat(str1,str2);
>     writeln(str1);
>     - - - - -
>     hello there

#### `StrDelete(string,N,position);`

This procedure deletes `N` characters from `string` . `Position` is an integer giving the subscript of the first character to be deleted.

>     str := "The quick brown fox";
>     StrDelete(str,2,5);
>     writeln(str);
>     - - - - -
>     The ick brown fox

#### `StrInsert(str1,str2,N);`

This procedure inserts the contents of `str2` into `str1` . The first character of `str2` will become character N of `str1` .

>     str := "The quick fox";
>     StrInsert(str1,"brown",5);
>     writeln(str);
>     - - - - -
>     The brownquick fox

#### `i := StrLen(string);`

This function returns an integer giving the length of a string. For a string constant, this is the number of characters in the constant. For a string variable, it is the number of characters currently stored in the variable. If the variable is not full, it will be the number of characters up to but not including the `StrLen` that follows the last character in the string.

>     str := "abcdef";
>     writeln(StrLen(str));
>     str := "abc";
>     writeln(StrLen(str));
>     writeln(StrLen("xyz");
>     - - - - -
>     6
>     3
>     3

#### `i := StrScan(str1,str2);`

This function looks for the string `str2` inside `str1` . If it is found, `StrScan` returns the subscript where `str2` begins in `str1` . If the string is not found, `StrScan` returns 0.

>     i := StrScan("hello there","the");
>     writeln(i);
>     - - - - -
>     7

#### `i := StrSize(string);`

This function returns an integer giving the maximum length of a string. If the string is a string constant, this is the number of characters in the string. If the string is a string variable, this is the maximum number of characters that the string can hold, regardless of how many it holds at the moment.

>     var
>         str : packed array [1..10] of char;
>       ...
>     writeln(StrSize("abc"));
>     str := "abc";
>     writeln(StrSize(str));
>     - - - - -
>     3
>     10

#### `SubStr(str1,N,position,str2);`

This procedure obtains a copy of part of `str1` . `N` is the length of the substring and `position` is the subscript where the substring starts. The substring that is obtained is copied into `str2` .

>     str1 := "Hello there";
>     SubStr(str1,2,3,str2);
>     writeln(str2);
>     - - - - -
>     ll

### File Routines

The following routines deal with I/O to and from files.

#### `append(filevar,filename);`

This procedure is much like `rewrite` in that it opens a file for writing. The file is associated with the file variable `filevar` . The name of the file is given by the string `filename` . If the `filename` argument is omitted, Alice will use the file variable name as the file name.

The difference between `append` and `rewrite` is that `append` sets up for writing at the end of the current contents of the file. Thus, new output is added to what the file already contains. With `rewrite` , output is written over the file's current contents, and those previous contents are lost.

#### `update(filevar,filename);`

This procedure opens a file for direct access reading and writing. The `filevar` argument is the file variable that will be associated with the file. The `filename` argument is a string that gives the name of the file. If this string is omitted, Alice will use the name of the file variable as the file name.

Opening a file for direct access I/O does not let you add new elements to the end of the file, but it lets you read and write existing elements in any order. When `update` is issued, the file is positioned at the beginning and an initial `get(filevar)` is performed. This means that the buffer variable `filevar^` will contain the value of the first element of the file. You may move to any other element of the file with the `setnext` procedure.

#### `setnext(filevar,record);`

This procedure moves to an arbitrary record in a direct access file. The `filevar` argument must be a file variable that has been opened with the `update` procedure. The `record` argument is an integer indicating the number of the file element to which you wish to move. The first element in the file is numbered 0.

### Terminal I/O

The following routines deal with I/O to and from the terminal.

#### `bool := Char_Waiting;`

This function returns a Boolean value indicating whether or not there is a character waiting to be read from the keyboard. It returns ``true'' if there is a character waiting and ``false'' otherwise.

#### `Cursor_To(Row,Column);`

This procedure moves the cursor to a particular position on the terminal screen. `Row` is an integer giving the row to which the cursor should be moved. The top row is numbered 0. `Column` is an integer giving the column to which the cursor should be moved. The leftmost column is numbered 0. Thus `Cursor_To(0,0)` puts the cursor in the upper left hand corner while `Cursor_To(13,40)` moves the cursor to about the middle of the screen.

#### `c := Get_Char;`

This function gets a single character from the keyboard. If there isn't an input character already waiting, `Get_Char` waits for one to be entered. Unlike `read` and `readln` , `Get_Char` obtains a character immediately -- it does not wait for an entire line to be entered. `Get_Char` cannot detect end-of-file.

You should avoid mixing calls to `Get_Char` and `Char_Waiting` with calls to standard I/O functions like `readln` , `get` , `writeln` , etc., since they perform I/O in entirely different ways. (Standard I/O functions buffer I/O, while `Get_Char` does not.)

#### `i := ScrXY(code);`

This function returns integer values that provide information about the terminal screen. The `code` argument is an integer indicating what kind of information you want.

> ScrXY(1) -- tells how many rows the screen has.

> ScrXY(2) -- tells how many columns the screen has.

> ScrXY(-1) -- row of current cursor position (top=0).

> ScrXY(-2) -- column of current cursor position (left=0).

#### `Set_Attr(code);`

This procedure is used to set terminal attributes when your program is writing output. The argument of `Set_Attr` is an integer indicating the attribute you want to set (colour, bold face, reverse video, etc.). The integer is interpreted as a bit pattern, according to the following key:

#### Bits 0-2 (values 1-7)

colour; on a monochrome screen, 1 means underlining; colour codes are

0 -- black

1 -- blue

2 -- green

3 -- cyan

4 -- red

5 -- magenta

6 -- brown

7 -- white

#### Bit 3 (value 8)

bold face (brighter for colour)

#### Bit 5 (value 32)

"reverse video" (black on white background)

#### Bit 6 (value 64)

black on coloured background; colour given by bits 0-2

#### Bit 7 (value 128)

blinking output

For example, a code of octal 054 would give reverse video bold red.

### Pointers

Alice supports a number of built-in routines for obtaining and using pointers into memory. A number of these make use of the new built-in named type `Pointer` .

The `Pointer` type is assignment compatible with all other Pascal pointers. The `nil` pointer is a good example of the `Pointer` type; it may be assigned to any other pointer.

Alice lets you assign string constants to `Pointer` variables. In this case, the string is created in memory and the `Pointer` variable receives a pointer to where that string is stored. For example, you might have

>     type
>         str10 = packed array [1..10] of char;
>     var
>         p : Pointer;
>         sp : ^str10;
>     begin
>     p := "Hello";
>     sp := p;
>     writeln(sp^[1]);
>     - - - - -
>     H

#### `i := address(variable);`

This function obtains the actual memory address of its argument. The argument must be a variable name, a subscripted variable, a field inside a record variable, or an indirectly-referenced variable. `address` returns an integer that gives the machine address that has been allocated to that variable. For example,

>     i := address(v);

obtains the address of the variable `v` .

You can also specify a second argument for `address` , indicating whether you want the segment containing the variable or the offset of the variable within the segment. If the argument is the integer 0, you get the offset; if it is any other integer, you get the segment. If this second argument is omitted, `address` returns the offset. For example,

>     i := address(v,1);

obtains the segment that contains the variable `v` .

#### `p := MakePointer(variable);`

This function is much like `address` except that it returns a `Pointer` type value instead of an integer. This can be assigned to any valid Pascal pointer type. Unlike `address` , `MakePointer` does not take an optional second argument. Alice Pascal pointers are constructed to give both the segment and offset of a particular memory location.

#### `i := peek(address);`

This function returns the value that is at the given memory address. The `address` argument is given as an integer. The value that is returned is a single byte and will therefore be in the range 0..255. On the IBM PC, only the first 64K may be accessed this way. To access other addresses, use `RawPointer` (described below).

#### `poke(address,value);`

This procedure stores a value in the given address. The `address` is given as an integer. Since `poke` only stores a single byte, the `value` argument should be in the range 0..255. If it is not, `poke` will use `value mod 256` . On the IBM PC, only the first 64K may be accessed this way. To access other addresses, use `RawPointer` (described below).

#### `p := RawPointer(offset,segment);`

This function converts an integer address into a Pointer type. The `offset` argument is an integer giving an offset within a segment, and the `segment` argument is an integer giving a segment number. If the `segment` is omitted, 0 is assumed. The value returned by `RawPointer` has the `Pointer` type.

### Random Number Generation

#### `initrandom(seed,upper);`

This procedure initializes a pseudo-random number generator. Both `seed` and `upper` are integers. Different values of `seed` will give different number sequences; identical values of `seed` will give identical sequences. Random numbers will be generated in the range `0..(upper-1)` .

#### `i := random`

This function returns a pseudo-random integer. You may not call `random` until you have initialized the random number generator with `initrandom` .

### System Calls

#### `i := SysFunc(p,arg,arg,arg,...);`

This function calls a system function. The `p` argument is a `Pointer` type pointing to the memory location of the function to be called. The function is passed an argument count and a pointer to a vector of arguments. The value returned by `SysFunc` is the value returned by the system function. This routine is not intended for use by normal users.

#### `SysProc(p,arg,arg,arg,...);`

This procedure works the same way as `SysFunc` . It is used to call system routines that do not return a value.

### Miscellaneous Symbols

#### `Alice_Version`

This is a built-in constant giving the version number of the Alice you are using.

#### `pause;`

When Alice has ``Debug On'' and it encounters a call to the `pause` procedure, the effect is the same as if a breakpoint was triggered while running the program. The program will stop and wait for run-time commands (e.g. going into immediate mode). When Alice has ``Debug Off'', the `pause` procedure has no effect.

#### `i := SizeOf(variable);`

This function returns the number of bytes of storage that are allocated to a particular variable. With string variables, `SizeOf` is equivalent to `StrSize` .