Data Types [ HP Pascal/iX Reference Manual ] MPE/iX 5.0 Documentation
HP Pascal/iX Reference Manual
Data Types
Figure 11-2 summarizes the types that are supplied by the system
programming extensions. A detailed discussion of the data types follows
in this chapter. This figure augments the HP Pascal data types
summarized in Figure 11-1 . Note that the HP Pascal predefined data
types are highlighted.
Figure 11-2. Extended Data Types
Structured Types
CRUNCHED.
In Pascal, a structure (array, record, or set) can be unpacked or packed.
Packed structures are declared by specifying the reserved word PACKED at
the start of a structured type declaration.
The system programming extensions define a third type of packing in
addition to unpacked and packed, namely CRUNCHED.
The reserved word CRUNCHED indicates that the components of a structured
type (array, record, or set) are allocated contiguously, first to last,
in a bit-aligned fashion with no intervening unused bits. Syntactically,
the word CRUNCHED may be substituted for the word PACKED.
The primary purpose of crunched packing is to provide a map from
data item type to data representation that is independent of the
implementation and the packing algorithm. For that reason, machine
dependent types such as real, longreal, and file are not allowed in
crunched structures.
Example
TYPE
rec = RECORD
a : type_a;
b : type_b;
c : type_c;
END;
crec = CRUNCHED RECORD
a : type_a;
b : type_b;
c : type_c;
END;
In a crunched structure, each component is allocated the minimum number
of bits required to represent that type, and each component is aligned in
such a way that there are no unused bits between it and the previous
component.
The first declaration for rec in the previous example may lead to the
following storage allocation for an arbitrary processor:
Figure 11-3. Layout of a Record
Note that there are unused bits between the fields a and b, and between
the fields b and c.
The crunched record declaration for crec, that is identical to the
uncrunched record rec with the addition of the reserved word CRUNCHED,
would lead to the following storage allocation:
Figure 11-4. Layout of a Crunched Record
Note that there are no wasted bits between fields in the crunched record.
The number of bits used to represent each component of a crunched
structured type is the minimum needed to represent the values associated
with that component. The calculation of the minimum number of bits for
various types is:
* Record, Array Types.
The sum of the minimum number of bits required to represent
each component. If the record has variants, consider the
size of the largest variant.
* Set Types (of the form set of low .. high).
The ordinal value of high minus the ordinal value of low
plus one:
ord(high) - ord(low) + 1
* Char and Enumeration Based Types (of the form low .. high).
The next larger integer (the ceiling) of the logarithm base
2 of the successor of the ordinal value of the upper bound,
or one, whichever is greater:
max( ceil [ log2( ord(high) + 1 ) ], 1 )
Integer Based Types (of the form low .. high)
The next larger integer (the ceiling) of the logarithm base
2 of the maximum of the absolute value of the ordinal value
of the lower bound, and the successor of the absolute value
of the ordinal value of the upper bound, or one, whichever
is greater:
max( ceil [ log2( max( |low|, |high| + 1 ) ) ], 1 )
If the type is signed (the lower bound is less than zero),
then add one to the size.
Table 11-3 shows the lower and upper bound ranges and number of bits
allocated for unsigned subranges. Table 11-4 gives the same
information for signed subranges.
Table 11-3. Number of Bits Allocated for Unsigned Subranges
-----------------------------------------------------------------------------------------------
| | | |
| Lower Bound Range | Upper Bound Range | # Bits Allocated |
| | | |
-----------------------------------------------------------------------------------------------
| | | |
| >= 0 | 0..1 | 1 |
| >= 0 | 2..3 | 2 |
| >= 0 | 4..7 | 3 |
| >= 0 | 8..15 | 4 |
| >= 0 | 16..31 | 5 |
| >= 0 | 32..63 | 6 |
| >= 0 | 64..127 | 7 |
| >= 0 | 128..255 | 8 |
| >= 0 | 256..511 | 9 |
| >= 0 | 512..1023 | 10 |
| >= 0 | 1024..2047 | 11 |
| >= 0 | 2048..4095 | 12 |
| >= 0 | 4096..8191 | 13 |
| >= 0 | 8192..16383 | 14 |
| >= 0 | 16384..32767 | 15 |
| >= 0 | 32768..65535 | 16 |
| >= 0 | 65536..131071 | 17 |
| >= 0 | 131072..262143 | 18 |
| >= 0 | 262144..524287 | 19 |
| >= 0 | 524288..1048575 | 20 |
| >= 0 | 1048576..2097151 | 21 |
| >= 0 | 2097152..4194303 | 22 |
| >= 0 | 4194304..8388607 | 23 |
| >= 0 | 8388608..16777215 | 24 |
| >= 0 | 16777216..33554431 | 25 |
| >= 0 | 33554432..67108863 | 26 |
| >= 0 | 67108864..134217727 | 27 |
| >= 0 | 134217728..268435455 | 28 |
| >= 0 | 268435456..536870911 | 29 |
| >= 0 | 536870912..1073741823 | 30 |
| >= 0 | 1073741824..2147483647 | 31 |
| | | |
-----------------------------------------------------------------------------------------------
Table 11-4. Number of Bits Allocated for Signed Subranges
----------------------------------------------------------------------------------------------
| | | |
| Lower Bound Range | Upper Bound Range | #Bits Allocated |
| | | |
----------------------------------------------------------------------------------------------
| | | |
| -1 | 0 | 1 |
| -2 | 1 | 2 |
| -4..-3 | 2..3 | 3 |
| -8..-5 | 4..7 | 4 |
| -16..-9 | 8..15 | 5 |
| -32..-17 | 16..31 | 6 |
| -64..-33 | 32..63 | 7 |
| -128..-65 | 64..127 | 8 |
| -256..-129 | 128..255 | 9 |
| -512..-257 | 256..511 | 10 |
| -1024..-513 | 512..1023 | 11 |
| -2048..-1025 | 1024..2047 | 12 |
| -4096..-2049 | 2048..4095 | 13 |
| -8192..-4097 | 4096..8191 | 14 |
| -16384..-8193 | 8192..16383 | 15 |
| -32768..-16385 | 16384..32767 | 16 |
| -65536..-32769 | 32768..65535 | 17 |
| -131072..-65537 | 65536..131071 | 18 |
| -262144..-131073 | 131072..262143 | 19 |
| -524288..-262145 | 262144..524287 | 20 |
| -1048576..-524289 | 524288..1048575 | 21 |
| -2097152..-1048577 | 1048576..2097151 | 22 |
| -4194304..-2097153 | 2097152..4194303 | 23 |
| -8388608..-4194305 | 4194304..8388607 | 24 |
| -16777216..-8388609 | 8388608..16777215 | 25 |
| -33554432..-16777217 | 16777216..33554431 | 26 |
| -67108864..-33554433 | 33554432..67108863 | 27 |
| -134217728..-67108865 | 67108864..134217727 | 28 |
| -268435456..-134217729 | 134217728..268435455 | 29 |
| -536870912..-268435457 | 268435456..536870911 | 30 |
| -1073741824..-536870913 | 536870912..1073741823 | 31 |
| -2147483648..-1073741825 | 1073741824..2147483647 | 32 |
| | | |
----------------------------------------------------------------------------------------------
Example
TYPE
cr1_t = CRUNCHED RECORD
f1 : 0..15; { takes 4 bits }
f2 : -1..15; { takes 5 bits }
f3 : -16..15; { takes 5 bits }
f4 : 13..15; { takes 4 bits }
END; { total: 18 bits }
cr2_t = CRUNCHED RECORD
f1 : CRUNCHED set of 0..15; { takes 16 bits }
f2 : CRUNCHED set of 13..15; { takes 3 bits }
f3 : CRUNCHED set of -5..5; { takes 11 bits }
END; { total: 30 bits }
cr3_t = CRUNCHED RECORD
f1 : integer; { takes 32 bits }
CASE tag : Boolean OF { takes 1 bit }
true: ( v1 : cr1_t ); { takes 18 bits }
false:( v2 : cr2_t ); { takes 30 bits }
END; { total: 63 bits }
The restrictions that apply to packed types also apply to crunched types.
In particular, it is illegal:
* To pass a component of a crunched structure as a reference
parameter.
* To take the address of a component of a crunched structure.
In addition:
* File types cannot be crunched.
* Structured types that contain file, real, longreal, string, or
pointer types cannot be crunched.
* All structured types contained in a crunched structured type must
also be crunched.
* All integer based types and enumeration based types are
represented with the most significant bit first through least
significant bit last. Byte swapping is not permitted.
Pointer Types
In HP Pascal, pointers are designators that point only to a specific
class of objects, namely objects on the heap.
When using the system programming extensions, pointers can point to any
data; that is, objects on the heap, as well as global and local
variables. In this sense pointers truly are addresses.
In HP Pascal, the only way to create a pointer is by calling the
predefined procedure NEW or the intrinsic getheap to dynamically allocate
a heap object. In order to create pointers, the system programming
extensions define the addr function that returns the address of its
argument, and the functions buildpointer and addtopointer that perform
address arithmetic.
There are three predefined pointer types defined in the system
programming extensions that allow relaxed type checking of pointers.
These are anyptr, localanyptr, and globalanyptr.
Short and Long Pointers.
The system programming extensions define two classes of pointers: short
and long pointers.
Long pointers can point to any addressable object on the system (in this
sense addressable in terms of the representability of an address, as
opposed to allowed access rights).
Short pointers can point to a subset of the objects addressable by long
pointers. A subset of the object addressable by short pointers are
objects in the heap. By default, all user declared pointers are short
pointers.
The following diagram explains the relationship between these classes of
pointers.
Figure 11-5. Pointer Class Relationship
Note that in some implementations, long and short pointers may be
identical; in other words, the collection of objects that long and short
pointers can point to may be the same.
The compiler option EXTNADDR can be used to specify that a given user
defined pointer type is to be a long pointer.
Localanyptr.
The predefined type localanyptr is a pointer type that is assignment
compatible with any other pointer type. It can be used to defeat type
checking on pointers.
A pointer of any type can be assigned to a pointer of type localanyptr,
and a pointer of type localanyptr can be assigned to any pointer type.
However, since pointers of type localanyptr are not bound to a base type,
they cannot be dereferenced. (In order to dereference a pointer of type
localanyptr, it must first be type coerced or assigned to a proper
pointer type).
localanyptr takes the form of a short pointer. It may only be able to
represent a subset of the addresses on a machine. On implementations
where short and long pointers are not the same, localanyptr is more
efficient than globalanyptr.
Permissible Operators
assignment :=
relational =, <>
Example
VAR
ptr1 : pointer_type_1;
ptr2 : pointer_type_2;
anyp : localanyptr;
BEGIN
...
anyp := ptr1;
anyp := ptr2;
...
ptr1 := anyp;
...
END;
Globalanyptr.
The predefined type globalanyptr is a pointer type that is assignment
compatible with any other pointer type. It can be used to defeat type
checking on pointers.
A pointer of any type can be assigned to a pointer of type globalanyptr,
and a pointer of type globalanyptr can be assigned to any pointer type.
However, since pointers of type globalanyptr are not bound to a base
type, they cannot be dereferenced. (In order to dereference a pointer of
type globalanyptr, it must first be type coerced or assigned to a proper
pointer type.)
Globalanyptr takes the form of a long pointer. It can represent any
address on the machine. A more efficient type of pointer called
localanyptr can be used in a program that has no need for long pointers.
Permissible Operators
assignment :=
relational =, <>
Example
VAR
ptr1 : pointer_type_1;
ptr2 : pointer_type_2;
anyp : globalanyptr;
BEGIN
...
anyp := ptr1;
anyp := ptr2;
...
ptr1 := anyp;
...
END;
Anyptr.
The predefined type anyptr is a pointer type that is assignment
compatible with any other pointer type. It can be used to defeat type
checking on pointers.
A pointer of any type can be assigned to a pointer of type anyptr, and a
pointer of type anyptr can be assigned to any pointer type. However,
since pointers of type anyptr are not bound to a base type, they cannot
be dereferenced. In order to dereference a pointer of type anyptr, it
must first be type coerced or assigned to a proper pointer type.
Anyptr takes the form of a long pointer. It can represent any address on
the machine. A more efficient type of pointer called localanyptr can be
used in a program that has no need for long pointers.
Anyptr is equivalent to globalanyptr; however, globalanyptr and
localanyptr are the recommended types to use.
Permissible Operators
assignment :=
relational =, <>
Example
VAR
ptr1 : pointer_type_1;
ptr2 : pointer_type_2;
anyp : anyptr;
BEGIN
...
anyp := ptr1;
anyp := ptr2;
...
ptr1 := anyp;
...
END;
The above example illustrates that a variable of type anyptr is
assignment compatible with any other pointer type.
Example
VAR
ptr1 : pointer_type_1;
ptr2 : pointer_type_2;
PROCEDURE proc( ptr : anyptr );
BEGIN
...
END;
BEGIN
proc( ptr1 );
proc( ptr2 );
END;
In the above example, the routine proc can accept any pointer as an
actual parameter because the type of the formal parameter is anyptr.
anyptr is assignment compatible with any pointer type.
Example
TYPE
pointer_type = ^record_type;
record_type = RECORD
int : integer;
END;
VAR
i : integer;
anyp : anyptr;
BEGIN
i := pointer_type( anyptr )^.int;
END;
In the above example, the pointer anyp is dereferenced to access a field
in a record. Since an anyptr is not bound to a base type, the pointer
must first be type-coerced to a pointer type corresponding to the
structure to which anyp is pointing.
PROCEDURE and FUNCTION Types
In Pascal, PROCEDURE and FUNCTION parameters allow dynamic reference to
procedures and functions where the exact instance of the procedure or
function is not known until run-time. The system programming extensions
extend this concept to allow variables as well as parameters that refer
to procedures and functions.
Syntax
A parallel can be drawn between pointers and PROCEDURE/FUNCTION
variables. While pointers are variables that reference data,
PROCEDURE/FUNCTION variables reference code.
Variables of PROCEDURE/FUNCTION types may be assigned procedures and
functions that have congruent parameter lists, as defined in HP Pascal.
See chapter 8 for more information on parameters. To assign a procedure
or function to a PROCEDURE/FUNCTION variable, the routine name is used as
a parameter to the addr function. See the section on predefined routines
in this chapter for more information on addr.
Any procedure or function assigned must have the same or wider scope than
the variable or value parameter to which it is assigned. Any
PROCEDURE/FUNCTION variable passed as a reference parameter must have the
same or wider scope than the formal parameter to which it is bound.
A PROCEDURE/FUNCTION variable can be assigned NIL.
The procedure or function referenced by a PROCEDURE/FUNCTION variable,
may be invoked by calling the predefined procedure call for a PROCEDURE
variable or the predefined function fcall for a FUNCTION variable. See
the section on predefined routines in this chapter for more information
on call and fcall.
Permissible Operators
assignment :=
relational =, <>
Standard Procedures
argument CALL
Standard Functions
argument FCALL
return ADDR
Example
TYPE
proc_0_type = PROCEDURE;
func_0_type = FUNCTION: integer;
proc_1_type = PROCEDURE( ANYVAR i : integer );
func_2_type = FUNCTION( VAR s : string;
i : integer ): boolean;
VAR
proc_0 : proc_0_type;
func_0 : func_0_type;
proc_1 : proc_1_type;
func_2 : func_2_type;
PROCEDURE p1; external;
PROCEDURE p2( n : shortint ); external;
PROCEDURE p3( VAR i : integer ); external;
BEGIN
func_0 := nil; { initialized to nil }
func_2 := nil; { initialized to nil }
proc_0 := addr( p1 ); { proc_0 now 'points to' p1 }
proc_1 := addr( p2 ); { illegal: parameters don't match }
proc_1 := addr( p3 ); { illegal: parameters don't match }
func_0 := addr( p1 ); { illegal: must be a function }
END.
Example
TYPE
proc_type = PROCEDURE;
VAR
proc_var_0 : proc_type;
PROCEDURE proc_1;
VAR
proc_var_1 : proc_type;
PROCEDURE proc_2;
BEGIN {PROCEDURE proc_2}
...
END; {PROCEDURE proc_2}
BEGIN {PROCEDURE proc_1}
proc_var_0 := addr( proc_1 );
proc_var_1 := addr( proc_1 );
proc_var_0 := addr( proc_2 ); { illegal: scoping violation }
proc_var_1 := addr( proc_2 );
END; {PROCEDURE proc_1}
MPE/iX 5.0 Documentation