Node: Schema Types, Next: Set Types, Previous: Variant Records, Up: Type Definition Possibilities

##### EP's Schema Types including `String`

Schemata are types that depend on one or more variables, called discriminants. They are an ISO 10206 Extended Pascal feature.

         type
           RealArray (n: Integer) = array [1 .. n] of Real;
           Matrix (n, m: PositiveInteger) = array [1 .. n, 1 .. m] of Integer;

The type `RealArray` in this example is called a Schema with the discriminant `n`.

To declare a variable of such a type, write:

         var
           Foo: RealArray (42);

The discriminants of every global or local schema variable are initialized at the beginning of the procedure, function or program where the schema variable is declared.

Schema-typed variables “know” about their discriminants. Discriminants can be accessed just like record fields:

         program Schema1Demo;
         type
           PositiveInteger = 1 .. MaxInt;
           RealArray (n: Integer) = array [1 .. n] of Real;
           Matrix (n, m: PositiveInteger) = array [1 .. n, 1 .. m] of Integer;

         var
           Foo: RealArray (42);

         begin
           WriteLn (Foo.n)  { yields 42 }
         end.

Schemata may be passed as parameters. While types of schema variables must always have specified discriminants (which may be other variables), formal parameters (by reference or by value) may be of a schema type without specified discriminant. In this, the actual parameter may posses any discriminant. The discriminants of the parameters get their values from the actual parameters.

Also, pointers to schema variables may be declared without a discriminant:

         program Schema2Demo;
         type
           RealArray (n: Integer) = array [1 .. n] of Real;
           RealArrayPtr = ^RealArray;
         var
           Bar: RealArrayPtr;
         begin
         end.

When applying `New` to such a pointer, you must specify the intended value of the discriminant as a parameter:

         New (Bar, 137)

As a GNU Pascal extension, the above can also be written as

         Bar := New (RealArrayPtr, 137)

The allocated variable behaves like any other schema variable:

         program Schema3Demo;
         type
           RealArray (n: Integer) = array [1 .. n] of Real;
           RealArrayPtr = ^RealArray;
         var
           Bar: RealArrayPtr;
           i: Integer;
         begin
           Bar := New (RealArrayPtr, 137);
           for i := 1 to Bar^.n do
             Bar^[i] := 42
         end.

Since the schema variable “knows” its size, pointers to schemata can be disposed just like other pointers:

         Dispose (Bar)

Schemata are not limited to arrays. They can be of any type that normally requires constant values in its definition, for instance subrange types, or records containing arrays etc. (Sets do not yet work.)

References to the schema discriminants are allowed, and the `with` statement is also allowed, so one can say:

         program SchemaWithDemo;
         type
           RealArray (n: Integer) = array [1 .. n] of Real;
         var
           MyArray: RealArray (42);
         begin
           WriteLn (MyArray.n);  { writes 42 }
           with MyArray do
             WriteLn (n);        { writes 42 }
         end.

Finally, here is a somewhat exotic example. Here, a `ColoredInteger` behaves just like an ordinary integer, but it has an additional property `Color` which can be accessed like a record field.

         program SchemaExoticDemo;

         type
           ColorType = (Red, Green, Blue);
           ColoredInteger (Color: ColorType) = Integer;

         var
           Foo: ColoredInteger (Green);

         begin
           Foo := 7;
           if Foo.Color = Red then
             Inc (Foo, 2)
           else
             Foo := Foo div 3
         end.

An important schema is the predefined `String` schema (according to Extended Pascal). It has one predefined discriminant identifier `Capacity`. GPC implements the `String` schema as follows:

         type
           String (Capacity: Cardinal) = record
             Length: 0 .. Capacity;
             Chars: packed array [1 .. Capacity + 1] of Char
           end;

The `Capacity` field may be directly referenced by the user, the `Length` field is referenced by a predefined string function `Length (Str)` and contains the current string length. `Chars` contains the chars in the string. The `Chars` and `Length` fields cannot be directly referenced by a user program.

If a formal value parameter is of type `String` (with or without discriminant), the actual parameter may be either a `String` schema, a fixed string (character array), a single character, a string literal or a string expression. If the actual parameter is a `String` schema, it is copied for the parameter in the usual way. If it is not a schema, a `String` schema is created automatically, the actual parameter is copied to the new variable and the `Capacity` field of the new variable is set to the length of the actual parameter.

Actual parameters to `var` parameters of type `String` must be `String` schemata, not string literals or character arrays.

         program StringDemo (Output);

         type
           SType = String (10);
           SPtr  = ^String;

         var
           Str : SType;
           Str2: String (100000);
           Str3: String (20) value 'string expression';
           DStr: ^String;
           ZStr: SPtr;
           Len : Integer value 256;
           Ch  : Char value 'R';

         { `String' accepts any length of strings }
         procedure Foo (z: String);
         begin
           WriteLn ('Capacity: ', z.Capacity);
           WriteLn ('Length  : ', Length (z));
           WriteLn ('Contents: ', z);
         end;

         { Another way to use dynamic strings }
         procedure Bar (SLen: Integer);
         var
           LString: String (SLen);
           FooStr: type of LString;
         begin
           LString := 'Hello world!';
           Foo (LString);
           FooStr := 'How are you?';
           Foo (FooStr);
         end;

         begin
           Str  := 'KUKKUU';
           Str2 := 'A longer string variable';
           New (DStr, 1000);  { Select the string Capacity with `New' }
           DStr^ := 'The maximum length of this is 1000 chars';
           New (ZStr, Len);
           ZStr^ := 'This should fit here';
           Foo (Str);
           Foo (Str2);
           Foo ('This is a constant string');
           Foo ('This is a ' + Str3);
           Foo (Ch);  { A char parameter to string routine }
           Foo ('');  { An empty string }
           Foo (DStr^);
           Foo (ZStr^);
           Bar (10000);
         end.

In the above example, the predefined procedure `New` was used to select the capacity of the strings. Procedure `Bar` also has a string whose size depends of the parameter passed to it and another string whose type will be the same as the type of the first string, using the `type of` construct.

All string and character types are compatible as long as the destination string is long enough to hold the source in assignments. If the source string is shorter than the destination, the destination is automatically blank padded if the destination string is not of string schema type.