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.