REF KEYS John Gibson May 1995
COPYRIGHT University of Sussex 1995. All Rights Reserved.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<< >>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<< STRUCTURE KEYS >>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<< >>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
This file describes the various predicates for use with keys and those
used to define and manipulate new classes of object defined by the user.
Two distinct kinds of new object class can be constructed: record-class
and vector-class. In addition, the specification of various other
special attributes for a class is dealt with.
This file also describes the production of procedures to access data
in external structures via external pointer-class records.
CONTENTS - (Use <ENTER> g to access required sections)
1 Introduction
1.1 Classes of Object
1.2 Accessing Keys of Items
1.3 External Structures
2 Predicates on Keys
3 Accessing Key Fields
3.1 Fields Applicable to All Keys
3.2 Record and Vector-type Key Fields
... General
... Record-only
... Vector-only
4 Constructing New Keys
5 Field Specifiers for Poplog Data
5.1 Notes on Allocation of Fields in Records
6 Examples of Key Construction
7 Constructing External Access Procedures
8 Fields in External Structures
9 Additional Field Specifiers for External Data
10 Miscellaneous
---------------
1 Introduction
---------------
Every structure in the Poplog system has within it a field containing a
pointer to a key structure, which identifies the class of the structure,
e.g. vectors contain a pointer to vector_key, procedures a pointer to
procedure_key, and so on. Keys themselves are structures, and so are
also identified by a key (key_key). So for every structure class in the
Poplog system (see REF * DATA), there is an identifying key structure.
For completeness, there are also key structures for the simple integer
and decimal data types.
As well as identifying the class of any object to the system, keys
also serve as a means of holding various information about the class,
e.g. which procedure is used to print objects in the class, which
procedure is to be used by the operation = in comparing them, and which
procedures are used to manipulate fields in the structure.
The latter is, in particular, the means of providing procedures to
manipulate new classes of object defined by the user. The basic process
of defining a new class of object consists of creating a new key for the
class with the procedure conskey described below; the appropriate
procedures to construct and manipulate the new objects are then
available from the key via the class_ procedures (also described later).
1.1 Classes of Object
----------------------
Two distinct kinds of new object class can be constructed: record-class
and vector-class. A record is a structure containing a fixed number of
distinct and possibly different fields, whereas a vector consists of a
variable number of similar fields. (For example: a pair is a
record-class, whereas vectors and strings are vector-types. The built-in
classes in the system also include other types which do not fall into
these categories and which cannot be user-defined, e.g. keys,
procedures, processes, etc.)
In addition to the record/vector distinction, various other special
attributes may be specified for a class. These include the 'writeable'
and 'nonwriteable' attributes (referring to the class' treatment by
sys_lock_system), and (for records only), "external_ptr" attribute
(which enables records of suitable format to behave as pointers to
external data).
Finally on Poplog structures, note that the basic method of class
construction using conskey as described here is not very convenient for
most normal programming contexts; a much more convenient interface
(which enables keys to be specified, constructed, and their class_
procedures automatically assigned to identifiers) is provided by the
syntax construct * defclass. This is described in REF * DEFSTRUCT
1.2 Accessing Keys of Items
----------------------------
The key of any object is accessed with the procedure datakey (or, from
an object's dataword, with key_of_dataword). See REF * DATA
Note that for each built-in data-type there is a global permanent
constant whose name starts with the dataword and ends with _key, and
whose value is the key for that type, e.g. integer_key, ratio_key,
biginteger_key, device_key etc.
1.3 External Structures
------------------------
This file also describes the production of procedures to access data in
external structures via external pointer-class records. The basic
procedure for this is cons_access, although as with Poplog structures, a
more convenient programming interface is provided by the syntax
construct defexacc, also described in REF * DEFSTRUCT
---------------------
2 Predicates on Keys
---------------------
(See also class_attribute for determining special attributes of keys.)
iskey(item) -> bool [procedure]
Returns true if item is a key, false otherwise.
-----------------------
3 Accessing Key Fields
-----------------------
The following procedures can be divided into those which are applicable
to all key fields and those only applicable to record and vector-type
key fields only.
3.1 Fields Applicable to All Keys
----------------------------------
class_=(key) -> equal_p [procedure]
equal_p -> class_=(key)
For any class key key, returns or updates the procedure used by
= in comparing items of the class represented by key.
The default value for any key is the procedure run by sys_=. For
the updater, if sys_= is supplied as equal_p then the default
procedure is restored. See REF * DATA
class_apply(key) -> apply_p [procedure]
apply_p -> class_apply(key)
Returns the apply procedure for the class key key. If an object
X of the class is applied (as if it were a procedure), what
happens is that apply_p is called instead, with X as argument.
E.g. if the variable X contains a structure of class key then
the call X() will turn into apply_p(X). Similarily for an
updater call, i.e.-> X() will result in -> apply_p(X).
The updater of class_apply assigns the procedure apply_p to
be the apply procedure for the class key key. (This mechanism
will NOT change what happens when an actual procedure is
applied, i.e. assigning to the class_apply of the procedure key
has no effect.)
WARNING: class_apply is the means by which array-type indexed
access on lists, words, vectors and strings is implemented, i.e.
the facility to use ITEM(N) for subscrX(N, ITEM). In general,
the class_apply for these data types is a procedure like
procedure(item);
if stacklength() /== 0 and isinteger(dup()) then
subscrX(item)
else
mishap(item, 1, 'EXECUTING NON-PROCEDURE')
endif
endprocedure;
and similarily for the updaters. Since this facility is used
WIDELY in both system and library procedures, you can expect
trouble if you redefine class_apply for any of these data types.
class_dataword(key) -> word [procedure]
Returns the dataword of the class key key.
class_hash(key) -> hash_p [procedure]
hash_p -> class_hash(key)
For any class key key returns or updates the hashing procedure
for objects of class key, invoked by syshash. The hashing
procedure takes an object of the class key and returns an
integer determined by the object, subject to the constraints
that if two objects are = then they should be assigned the same
value by the hashing procedure. The default hashing procedures
are described in REF * PROPS
class_print(key) -> print_p [procedure]
print_p -> class_print(key)
For any class key key, returns or updates the procedure used by
syspr to print an item in that class, the default value for any
key being sys_syspr. See REF * PRINT
class_recognise(key) -> recognise_p [procedure]
Returns the recogniser procedure of the class key key, i.e. a
procedure which returns true when applied to a member of the
class, false when applied to anything else.
class_field_spec(key) -> spec [procedure]
Returns the specification of the class key key. For a vector
class this is a single field specifier, for a record class it is
a list of them (see Field Specifiers for Poplog Structures below
for a description of field specifiers). For any other type it
returns false.
(Thus the correct test for a record class is islist(spec); note
however that the length of its spec list is NOT the correct way
to determine how many fields are in the record; datalength(key)
should be used for this.)
class_attribute(key, attribute) -> info [procedure]
class_attribute(key) -> attribute_list
Requests information about the attributes of the class key key.
In the first form, attribute may be one of the following (see
conskey below for more details on some of these attributes):
"byte_access"
Returns true if structures of this class are
byte-accessible, false otherwise (see Byte-Accessible
Structures in REF * DATA).
"external_deref"
Returns true if this an external dereference class key,
false otherwise.
"external_ptr"
Returns true if this an external pointer-class key,
false otherwise (this attribute also implies
"external_deref").
"external_noconv"
Returns true if objects of this class undergo no
conversion when passed externally (or assigned into an
"exval" field), false otherwise. That is, true is
returned for anything which is not "external_ptr",
"external_deref", (big)integer or (d)decimal.
"external_ptr_props"
Returns true if this an external pointer-class key
having the additional property that external_ptr_props
can be applied to records of the class, false otherwise.
"writeable"
Returns true if this class of structures are always
writeable by default, false if always non-writeable by
default, and "undef" otherwise.
"prop_entry"
For a property entry key, returns the property entry
gc_type (see REF * PROPS), or false otherwise.
In the second form with just a key argument, class_attribute
returns the list of attributes that would be given to conskey to
produce this key. That is, the list contains one or other (or
neither) of "writeable" or "nonwriteable", and one or other (or
neither) of "external_ptr" or "external_deref".
class_spec(key) -> spec [procedure]
class_spec(key, attribute) -> info
This is an old combined version of class_field_spec and
class_attribute.
In the first form it returns the class_field_spec of key.
However, to maintain upward compatibility with versions of
Poplog prior to 14, when applied to a vector class it returns
not the actual field specifier but the value_spec result of
field_spec_info applied to it, i.e.
field_spec_info(class_field_spec(key)) -> (value_spec, );
(This means for example that class_spec(string_key) is 8 rather
than "byte".) For a recordclass or any other kind of key, it is
identical to class_field_spec.
The second form is the same as class_attribute(key, attribute).
3.2 Record and Vector-type Key Fields
--------------------------------------
The following procedures can be divided into those which apply to both
types of key fields, and those which apply to record or vector-type key
fields only.
... General
------------
class_cons(key) -> cons_p [procedure]
For a record or vector-type class key key, returns the
constructor procedure for that class. For a record-type class,
this procedure takes N arguments, where N is the the number of
fields in the record; for a vector-type class, it takes an
argument N and constructs a vector of length N with N items
taken off the stack (cf conspair for records, consvector or
consstring for vectors).
class_dest(key) -> dest_p [procedure]
For a record or vector-type class key key, returns the
destructor procedure for that class (cf destpair for records,
destvector or deststring for vectors).
... Record-only
----------------
class_access(n, rec_key) -> access_p [procedure]
For a record-type class key rec_key, returns the access
procedure for the n-th field of that record class, i.e. the
procedure that returns or updates the n-th field.
Since class_access is the class_apply of keys, this may also be
called as
rec_key(n) -> access_p
See also class_fast_access (below).
class_fast_access(n, rec_key) -> access_p [procedure]
Uses cons_access to create a non-checking access/update
procedure for the n-th field of data structures with key
rec_key. See also class_access (above).
class_datasize(rec_key) -> n [procedure]
For a record-type class key rec_key, returns the length in words
of the record.
... Vector-only
----------------
class_init(vec_key) -> init_p [procedure]
For a vector-type class key vec_key, returns the initialiser
procedure for that class, i.e. a procedure that takes an integer
N and constructs a new vector of length N (i.e. initv or inits,
etc). This has its components initialised to "undef" for a full
vector, zero for a non-full one. (See also initvectorclass in
REF * DATA, which initialises a vector for a given class key,
but with a specified initialising value.)
class_subscr(vec_key) -> subscr_p [procedure]
For a vector-type class key vec_key, returns the subcripting
procedure subscr_p for that class, i.e. a procedure with updater
of the form
subscr_p(N, vec) -> element
element -> subscr_p(N, vec)
which returns or updates the N-th element of a vector in that
class (cf subscrv, subscrs).
Note that since the class_subscr procedure of a vectorclass
is by default its class_apply, a vector of the class can also be
subscripted by
vec(N) -> element
element -> vec(N)
class_fast_subscr(vec_key) -> subscr_p [procedure]
Like class_subscr, but subscr_p is a non-checking subscriptor
procedure.
------------------------
4 Constructing New Keys
------------------------
conskey(word, spec) -> vec_key [procedure]
conskey(word, spec, attribute_vec) -> vec_key
conskey(word, spec_list) -> rec_key
conskey(word, spec_list, attribute_vec) -> rec_key
Constructs and returns a key for a new record or vector class of
structures, with dataword word and specification as given by
spec or spec_list. The optional attribute_vec argument, if
present, is a vector of attribute names (words) specifying
additional information about the new class of structures.
For a vector class, spec is a single field specifier (all
elements of a vector having the same specification).
For a record class, spec_list is a list of field specifiers,
where each specifier in the list refers to one field in the
record, i.e. the record has length(spec_list) fields. (An
exception to this is when spec_list contains the 'dummy' field
specifier ">->"; in this case, the record will have
length(spec_list)-1 fields.)
Permissible field specifiers in either case are described below
under Field Specifiers for Poplog Structures.
Additional attributes for the key that can be specified by words
in the attribute_vec argument are (currently) as follows:
writeable, nonwriteable
Sets the default treatment by sys_lock_system (and also
Popc compilation) for the new class of structures:
"writeable" means always keep in writeable memory (so
fields can be updated), "nonwriteable" means put in
write-protected memory (so no updating is possible).
Note that when neither is specified for a class, the
general default given to sys_lock_system or Popc will
apply. See REF * SYSTEM
external_deref (Records Only)
To have this attribute, a record class must have a
"word"-sized field at the pointer position (see Format
of Data Structures in REF * DATA). When a record of the
class is passed to an external function (or assigned
into an "exval" field), the value of that word field is
used in its place. See REF * EXTERNAL
external_ptr (Records Only)
To have this attribute, a record class must have an
"exptr" field at the pointer position (see Format of
Data Structures in REF * DATA); records of the class are
then deemed to be pointers to external data via that
field. This means that external structure access
procedures can be used on them, or they can be assigned
into "exptr" or "exval" fields in other structures (they
are also recognised by isexternal_ptr_class). See
REF * EXTERNAL_DATA
This attribute also implies "external_deref", i.e. a
records's "exptr" field is passed in its place when
given as argument to an external function.
(Note also that if the record class starts with a
"full" field, i.e. its spec_list begins
[full exptr ... ]
etc, then external_ptr_props can be applied to records
of the class, and accesses the "full" field.
class_attribute can be used to test for this property on
a given key.)
(The values of the above attributes for existing keys can be
determined with class_attribute, see above.)
-----------------------------------
5 Field Specifiers for Poplog Data
-----------------------------------
This section lists the permissible field type specifiers for Poplog
data, i.e. that can appear in the spec or spec_list argument to conskey
(spec_list is a list of type specifiers for a record class, and spec is
a single one for a vector class).
Full Poplog Item
This is the quickest field type to access or update, since it
requires no conversion to and from Poplog representation, and no
check on values assigned into it, etc (see Representation of Data in
Poplog in REF * DATA).
Type Meaning
---- -------
"full" Holds a single Poplog item, and occupies one
'natural' machine word (64 bits in Alpha OSF, 32 in
all other current implementations).
'Packed' Integers
These fields can contain integers only, either signed or unsigned,
represented in a fixed number of binary bits.
When accessed, such a field produces a Poplog simple integer or
a biginteger if the field value is too large for a simple integer.
Similarily, a simple integer or biginteger within the range allowed
can be assigned into the field.
The named types correspond to standard sizes on the host machine
(and are always aligned on appropriate boundaries to be efficiently
accessible), whereas fields specified as a specific number of bits
(i.e. N or -N) are 'bitfields' and are generally slower to
access/update (and in a structure, simply occupy the next N bits).
Type Meaning
---- -------
"longlong" Signed integer, as C compiler type 'long long' (64
bits in all current implementations). Range
-2**63 <= I < 2**63
"ulonglong" Unsigned integer, as C compiler type 'unsigned long
long' (64 bits in all current implementations).
Range
0 <= I < 2**64
"word" Signed integer the size of a Poplog word (64 bits in
Alpha OSF, 32 in all other current implementations).
Range
-2**63 <= I < 2**63 (Alpha OSF)
-2**31 <= I < 2**31 (others)
"uword" Unsigned integer the size of a Poplog word (64 bits
in Alpha OSF, 32 in all other current
implementations). Range
0 <= I < 2**64 (Alpha OSF)
0 <= I < 2**32 (others)
"long" Signed integer, as C compiler type 'long' (64 bits
in Alpha OSF, 32 in all other current
implementations). Range
-2**63 <= I < 2**63 (Alpha OSF)
-2**31 <= I < 2**31 (others)
"ulong" Unsigned integer, as C compiler type 'unsigned long'
(64 bits in Alpha OSF, 32 in all other current
implementations). Range
0 <= I < 2**64 (Alpha OSF)
0 <= I < 2**32 (others)
"int" Signed integer, as C compiler type 'int' (32 bits in
all current implementations). Range
-2**31 <= I < 2**31
"uint" Unsigned integer, as C compiler type 'unsigned int'
(32 bits in all current implementations). Range
0 <= I < 2**32
"short" Signed integer, as C compiler type 'short' (16 bits
in all current implementations). Range
-2**15 <= I < 2**15
"ushort" Unsigned integer, as C compiler type 'unsigned
short' (16 bits in all current implementations).
Range
0 <= I < 2**16
"sbyte" Signed byte (8 bits in all current implementations).
Range
-2**7 <= I < 2**
"byte" Unsigned byte (8 bits in all current
implementations). Range
0 <= I < 2**
-N Signed bitfield of N bits, where 1 <= N <= 32. Range
-2**(N-1) < = I < 2**(N-1))
N Unsigned bitfield of N bits, where 1 <= N <= 32.
Range
0 <= I < 2**
"pint" Same as "word", but declares the field as holding
only values within the range of a Poplog simple
integer (pop_min_int <= I <= pop_max_int). When this
is known for a "word" field, using "pint" instead
gives faster access/update.
(N.B. All vector classes constructed on the types "byte" and "sbyte"
are special insofar as they are guaranteed to be null-terminated,
i.e. to have a 0 byte following the last actual byte of the vector.
This costs on average an extra byte per vector, but allows data such
as standard strings to be passed to external C functions without
modification.)
Floating-Point
Any non-complex number, including integers and ratios, can be
assigned into these fields, conversion and/or rounding being done
where necessary (but a mishap will occur if the input value is
outside the range of the field).
Accessing an "sfloat" or "float" field always produces a Poplog
decimal; accessing a "dfloat" field produces a decimal if
popdprecision is false and a decimal can contain the value, or a
ddecimal otherwise (see REF * popdprecision).
Type Meaning
---- -------
"dfloat" A double-length floating-point number in machine
format (64 bits in all current implementations).
"sfloat" A single-length floating-point number in machine
format, (32 bits in all current implementations).
"float" Identical to "sfloat", EXCEPT when specified as an
external function result -- see Additional Field
Specifiers for External Data below.
External Data & Pointers
There are two fields of this type: "exptr" and "exval". The first
holds a raw pointer value derived from some external source, while
the second holds any external value (which may or may not be a
pointer).
On accessing the field, the value (pointer or not) is returned
inside an external pointer record. This record will be constructed
anew each time the field is accessed, EXCEPT when the "exptr" or
"exval" spec is the sub_spec of a conversion procedure (see below),
or, for "exptr", the sub_spec of an implicit access procedure (see
Additional Field Specifiers for External Data below). In these
cases, a fixed record is used (avoiding the creation of unnecessary
garbage when the (pointer) value is being passed directly to such a
procedure -- 'fixed' here means that the same record is always used
for one particular field, but not the same one for all fields).
When updating, either type of field allows the pointer value
from any external pointer-class record to be assigned in.
However, for "exval" only, any Poplog object can be assigned
into the field. The actual value inserted is got by processing the
object as if it were an argument to an external function call (see
Calling External Functions, External Function Argument Processing in
REF * EXTERNAL for full details). Note, however, that there are two
differences from the latter: (1) (d)decimals are always coerced to
single float (since the field only occupies 1 word), and (2) Pop
data structures whose direct address will be the value MUST be
fixed-address (that is, any item for which
class_attribute(datakey(item), "external_noconv")
is true).
Type Meaning
---- -------
"exptr" A pointer to external data, occupying one 'natural'
machine word (64 bits in Alpha OSF, 32 in all other
current implementations).
"exval" Some external data, occupying one 'natural' machine
word (64 bits in Alpha OSF, 32 in all other current
implementations).
See also REF * EXTERNAL_DATA
Conversion Procedure on Field
This specifies a field with an automatic 'conversion' procedure
built on top of it. The specifier itself is a closure of the form
conv_p(% sub_spec, true %)
where conv_p is the conversion procedure (with updater), and
sub_spec is the type specifier for the underlying field (which may
be anything described in this section, including another conversion
procedure). (The true frozval distinguishes a conversion procedure
spec from an implicit access procedure spec, which is only allowed
for the external access types described later in this file.)
On accessing the field, its value is
conv_p( <underlying field value> ) -> <converted field value>
that is, conv_p applied to the value of the underlying field; on
updating, the field must have a converted value assigned into it,
and the updater of conv_p is expected to convert this back to an
underlying field value, i.e.
-> conv_p( <converted field value> ) -> <underlying field value>
(N.B. In compiling code for a conversion procedure (and external
implicit access procedures), the system guarantees to optimise out
calls to both identfn itself and closures of identfn of no arguments
(identfn(%%)). This is of particular relevance to using a conversion
procedure to type-check assignments into "full" fields; the access
side of such a procedure can simply be identfn(%%), resulting in no
overhead on accessing the field.)
5.1 Notes on Allocation of Fields in Records
---------------------------------------------
In a record class, fields are allocated space in the order specified,
treating the record as a sequence of bits, and starting at bit 0.
However, as described in Format of Data Structures in REF * DATA, the
key field occupies the second word of any structure (the relevant
diagram from REF * DATA is reproduced below):
+ +------------+
| | field(s) | word 1
header | |------------|
| | key | word 2
+ |------------|
>-> | | word 3
| remaining |
| field(s) |
Therefore, if a field would straddle the key (i.e. start in the the
first word and finish in the second), it is instead started at the
third. (Thus for example, the record spec_list
[byte short byte int]
is preferable to
[byte short int byte]
since it occupies 1 less word.)
Also (as shown in the diagram by ">->"), pointers to a structure
actually point to the third word, not the first; this is to allow e.g.
an externally-processed structure to appear to consist only of the
contiguous set of fields from its pointer onwards. For such
applications, it may be necessary to prevent some or any fields being
allocated in the first word.
This is achieved by including the 'dummy' field spec ">->" in a
record spec_list, which causes the following field to skip to the
pointer position, i.e. the third word (it is an error if this position
has already been passed when ">->" is encountered). E.g.
[>-> byte short byte int]
starts the first "byte" field at the pointer (and makes no use of the
first word).
Whereas a bitfield of N bits (specified with N or -N) is just
allocated to the next N bits, all other named field types imply some
sort of alignment appropriate to the type; for example, a "full" field
is always started at the next word boundary, and other named types at
least at the next byte boundary. (The actual alignments are such as to
be consistent with "struct" definitions in the C language on the host
machine.)
Finally, note that the length of a record is always rounded up to an
exact number of words.
-------------------------------
6 Examples of Key Construction
-------------------------------
The following defines a new user vector-type class whose components are
unsigned short integers:
conskey("ushort", "ushort") -> ushort_key;
class_cons(ushort_key) -> consushort;
consushort(32, 64, 128, 3) -> a_ushort;
a_ushort =>
** <ushort 32 64 128>
This corresponds to the defclass definition (see REF * DEFSTRUCT
defclass ushort :ushort;
etc.
The next example defines a new user record-type class with 4
different fields, representing a person's name, address, age and a field
to indicate sex (0=male, 1=female):
conskey("person", [full full byte 1]) -> person_key;
class_cons(person_key) -> consperson;
;;; get the access procedures for each field
person_key(1) -> person_name;
person_key(2) -> person_address;
person_key(3) -> person_age;
person_key(4) -> person_sex;
consperson('Fred Bloggs', 'No fixed abode', 99, 0) -> fred;
person_name(fred) =>
** Fred Bloggs
person_age(fred) =>
** 99
This corresponds to the defclass example given in REF * DEFSTRUCT
defclass person
{ person_name,
person_address,
person_age :byte,
person_sex :1
};
------------------------------------------
7 Constructing External Access Procedures
------------------------------------------
External structures are data maintained in memory outside the Poplog
system proper by external procedures, i.e. those written in non-Poplog
languages. Such data is represented and manipulated inside Poplog by
'external pointer-class' structures, which are ordinary Poplog
structures having an "exptr" field at the pointer position; access
procedures constructed by cons_access below can be applied to such
structures to extract or update external data via their pointers. (See
REF * EXTERNAL_DATA for a full explanation of external pointer-class
structures.)
As a special case, an external function or procedure can be thought
of as a data structure, where 'accessing' the structure means calling
the function to produce its result (if any). Thus cons_access also
constructs 'apply' procedures to call functions pointed to by external
pointers.
Because external structures (a) do not have to be made by Poplog
-style constructor procedures, (b) are not relocatable and always reside
in fixed memory locations, and (c) are not processed by the garbage
collector, they allow a greater range of field types than for native
Poplog structures. The additional field types allowed are described
below under Additional Field Specifiers for External Data; these include
fields which are sub-structures or arrays, as well as fields which
provide automatic access through pointers to underlying data.
cons_access allows a want or want_list argument to specify which
procedure(s) and their updaters are actually wanted. A single want
argument has the following values:
Value Meaning
----- -------
false or a No procedure (false is returned instead)
reference
containing false
A reference A procedure without an updater
containing a
non-false value
Anything else A procedure with an updater (if it can have
one)
A want_list argument is a then list of wants, one for each field in a
record or external structure.
Note that cons_access can also be used to construct 'fast' (i.e.
non-checking) access procedures for Poplog structures. (Also note that
in-line code corresponding to the procedures produced by cons_access can
be generated by the Poplog Virtual Machine instruction sysFIELD,
described in REF * VMCODE.)
cons_access(want_list, spec_list, check, mode) -> p_vec [procedure]
cons_access(want, spec_pair, check, mode) -> subscr_p
cons_access(want, spec_vec, check, mode) -> apply_p
cons_access(want, spec, check, mode) -> access_p
This procedure constructs
¤ field access procedures for external structures or Poplog
records (first form);
¤ a single subscriptor procedure for external arrays or
Poplog vectors (second form);
¤ a single apply procedure for external functions (third
form);
¤ a single access procedure for an external data type (last
form).
Which form is applicable is determined by whether the second
argument is a list, a pair, a vector, or anything else.
The mode argument
This is an integer, the bits of which control the type of access
as follows:
Bits Meaning
---- -------
0 - 7 An integer 0 - 255, specifying how many levels of
external pointer need to be dereferenced to reach
the data. A value of 0 indicates Poplog
record/vector accessing; a non-zero value (normally
1) indicates external accessing.
8 If set, then any access (external or Poplog)
producing an external pointer as result will produce
a fixed record every time. See the description of
the "exptr" field specifier under Field Specifiers
for Poplog Structures above.
9 External `address' mode: If set, then an external
access returns a pointer to the data rather than the
data itself; that is, an access to a structure field
or array element will return a pointer to the
component, etc. (This bit must be 0 for an external
function call.)
10 Like bit 8, but only applies to accesses for
external compound fields, i.e. fields which are
structures or arrays (where the access procedure
result is always an external pointer for the field
address).
For Poplog record/vector accessing, the mode argument may also
be false -- this is the same as 0.
The check argument
A boolean specifying in the external case only whether the
procedure(s) constructed should be fast (false) or checking
(true); in the latter case, all procedure(s) will check their
pointer argument to be an external pointer-class record, a
subscriptor procedure will check its array subscript, and a
variadic function call procedure will check its number of
arguments. (In the Poplog case this argument is ignored, all
procedure(s) being non-checking.)
First Form (Field Access Procedures)
For field access procedures, spec_list is a list of field
specifiers, allowable specifiers for Poplog records being as for
conskey (i.e. as described above under Field Specifiers for
Poplog Structures). For external structures, all these are
allowed plus those described in Additional Field Specifiers for
External Data below. In the external case, the structure MUST
also 'start at the pointer', that is, begin with the dummy field
spec ">->".
The want_list argument is a list of want values (as described
above), one for for each field in the structure/record; the
result p_vec is then a vector of the same length as want_list,
containing either a procedure or false in each element.
Second Form (Subscriptor Procedure)
For a subscriptor procedure, spec_pair is a pair of the form
conspair(element_spec, N)
where element_spec is a single field spec for the element type
of the external array or Poplog vector (allowable values in each
case as before). The N value is just ignored for a Poplog
vector, but for external, N is either an integer >= 0 for a
sized array (of N elements), or false for an unsized one. As
with vectors, the first element of an external array has
subscript 1; thus in the unsized case, a checking subscriptor
procedure just checks its subscript >= 1, but in the sized case
<= N as well.
The result subscr_p is then a subscriptor procedure or false,
depending on the want argument (as described above).
Third Form (External Apply Procedure)
For an external function apply procedure, spec_vec is a
2-element full vector of the form
consvector(N, result_spec, 2)
where (for a class of external functions with the same arguments
and result), N specifies the number of arguments and result_spec
the result type. (In this case, bits 0 - 7 of the mode argument
must be 1, and `address mode' must be clear.)
N is either an integer >= 0 for a function of N arguments, or
false for a variadic function. If exptr is an external
pointer-class record for the external function, then apply_p
will have the form
apply_p(arg1, ..., argN, exptr)
for fixed N, and
apply_p(arg1, ..., argN, N, exptr)
for the variadic case (i.e. the number of arguments must be
supplied as an additional last argument).
See REF * EXTERNAL for a description of how arguments are
processed when passed to external functions. From Version 14.2+,
the first element of spec_vec may also be a pair of the form
conspair(N, fltsingle)
where N is as above, and fltsingle is a (big)integer in which
bit B controls whether a (d)decimal supplied for the (B+1)-th
argument is passed as a machine single float (bit = 1) or a
double float (bit = 0). If spec_vec(1) is not a pair, fltsingle
defaults to 0.
result_spec may be any non-compound type (i.e. structures and
arrays are excluded), or "void" or false if the function does
not return a result.
The result apply_p is then an apply procedure or false,
depending on the want argument (as described above).
Last Form (Type Access Procedure)
For a single access procedure for an external data type, spec is
the specifier for the type, and the result access_p is then an
access procedure for that type or false, depending on the want
argument (as described above).
--------------------------------
8 Fields in External Structures
--------------------------------
As with Poplog records, an external structure is specified by a list of
field specifiers; this may occur either as the direct spec_list argument
to cons_access, or as a structure field in an outer structure or array,
etc. In either case, the structure must `start at the pointer', i.e.
begin with the dummy field specifier ">->". For example,
[>-> byte short byte int]
Fields in an external structure are allocated (and where necessary
aligned) in the same way as described in Notes on Allocation of Fields
in Records above, but starting at 0 offset (as indicated by ">->"). The
overall size of a structure is then obtained by rounding the finishing
offset to a multiple of the structure's alignment, that is, the greatest
alignment required by any of its fields. (This rounding ensures that the
correct alignment will be maintained throughout an array of structures.)
A structure can also be unsized, i.e. have as its last field an unsized
array (or other unsized structure).
To allow alternate versions of structures (i.e. like C `unions'),
the dummy field specifier "|" may appear anywhere in the list. This
resets the process of field allocation `back to the beginning', and must
always be followed by another ">->" to re-establish 0 offset from the
pointer, e.g.
[>-> byte short byte int | >-> dfloat int]
Thus the different alternatives overlay each other, and the size of the
structure is determined by the greatest finishing offset of any
alternative. (if any alternative is unsized, then the whole structure is
too). Similarily, the structure's alignment is the greatest required by
any alternative.
------------------------------------------------
9 Additional Field Specifiers for External Data
------------------------------------------------
This section lists the additional field type specifiers for external
data, i.e. that can appear in the spec_list, element_spec, result_spec,
or spec argument to cons_access with mode argument specifying an
external access.
All specifiers described under Field Specifiers for Poplog
Structures are allowed as well, of course. However, there is an
important point regarding "full" fields in external data: these must be
used with EXTREME caution. Unlike a record or vector class (which has a
key structure describing itself, used by garbage collection in
determining the position of "full" fields containing Poplog structure
pointers), external pointers contain no description of what they point
to. Thus a "full" field in an external data structure is NOT processed
by the garbage collector, and the assignment of a Poplog structure into
such a field may result in it containing junk after a garbage collection
-- this will certainly be the case if the structure is not fixed-address
(see Fixed-Address Poplog Structures for External Use in
REF * EXTERNAL_DATA). In an attempt to guard against this, an access
procedure for such a field mishaps if its value does not satisfy
is_poplog_item (which is by no means guaranteed to pick up all errors).
Array Field
As with the outer spec_pair argument to cons_access, an array
field is specified by a pair of the form
conspair(element_spec, N)
where element_spec gives the element type of the array, and N is
either an integer >= 0 for a sized array (of N elements), or false
for an unsized one.
Note that only the base field type of element_spec is relevant (that
is, any implicit access or conversion procedures are stripped off);
moreover, the base field type must be sized (i.e. it must not be an
unsized array, or a structure ending with one, etc). The field as a
whole then has size <base type size> * N if N is an integer, or is
unsized if N is false .
Within a structure, an array field will be aligned so as to accord
with the alignment (if any) necessary for its element type (see
Notes on Allocation of Fields in Records above). (Note that an
unsized array can only appear as the LAST field in a structure, in
which case that structure itself is unsized.)
On accessing an array field, its value is an external pointer record
pointing to the first element (which record is newly-constructed or
fixed in the same way as for an "exptr" field, qv). An array field
cannot be updated (thus an access procedure constructed by
cons_access for a field of this type never has an updater).
Structure Field
A structure field is specified by a list of field type specifiers
as described in Fields in External Structures above.
Within an (outer) structure or array, the size of a structure field
is that (sub-) structure's size, and its alignment is that (sub-)
structure's alignment. (An unsized structure field can only appear
as the last field in an outer structure, in which case the outer
structure is itself unsized.)
On accessing a structure field, its value is an external pointer
record pointing to the start of the sub-structure (which record is
newly-constructed or fixed in the same way as for an "exptr" field,
qv). A structure field cannot be updated (thus an access procedure
constructed by cons_access for a field of this type never has an
updater).
External Function
As with the outer spec_vec argument to cons_access, an external
function (or more properly, a set of possible functions with the
same number of arguments and same result type), is specified by a
full vector of the form
consvector(N, result_spec, 2)
or
consvector(conspair(N, fltsingle), result_spec, 2)
Here N is the number of arguments, or false for a variadic function
(i.e. where the number of arguments is supplied as an additional
last argument).
fltsingle (defaulting to 0 in the first form) is a (big)integer in
which bit B controls whether a (d)decimal supplied for the (B+1)-th
argument is actually passed as a single float (bit = 1) or a double
float (bit = 0).
result_spec is the result type: it may be any non-compound type
(i.e. structures and arrays are excluded), or "void" or false for no
result.
Note that IN THIS CONTEXT ONLY (i.e. as a result_spec), the field
specifier "float" behaves differently from "sfloat". Whereas the
latter assumes the result is always a single-float datum, "float"
assumes a result as returned by a "float" function in C. (This
distinction is necessary because on some systems, e.g. Suns, a C
"float" result is actually returned as a double. When calling C
functions, always use "float" rather than "sfloat".)
Aside from the outer argument to cons_access, an external function
can only appear as the spec in an implicit type access as described
below (that is, always specifying a POINTER to a function, never an
actual function).
Implicit Access Procedure on Pointer Field
This specifies a pointer field with an automatic access procedure
built on top of it. The specifier itself is a closure of the form
acc_p(% sub_spec, false %)
where acc_p is the access procedure, and sub_spec is the type
specifier for the underlying field (the false frozval distinguishes
an implicit access procedure from a conversion procedure).
The type specifier sub_spec must be something producing an external
pointer, i.e. an "exptr" field, a structure or array field, or
another implicit access producing a pointer. (Note that it cannot be
an "exval" field.)
On accessing the field, its output value is
acc_p( <underlying field pointer> ) -> <value>
that is, whatever results from applying acc_p to the pointer
produced from the underlying field. The field is updateable ONLY if
acc_p possesses an updater; in this case, the field is updated by
applying the updater of acc_p to the input value and the pointer
from the underlying field, i.e.
<value> -> acc_p( <underlying field pointer> )
(If acc_p has no updater then an access procedure constructed by
cons_access for the field will not have one either).
Implicit Type Access on Pointer Field
A special kind of implicit access 'procedure' allows the
access/update code for any type to be generated directly (without
the overhead of having it as a separate procedure call). When acc_p
is a closure of the form
identfn(% ":", spec %)
(that is, the field specifier as a whole consists of
identfn(% ":", spec %)(% sub_spec, false %)
), it specifies an implicit (or 'indirect') access for the type
given by spec (which can be anything). In this case, however,
sub_spec can be only an "exptr" field or another implicit type
access leading to an "exptr" field value (that is, a type access
cannot be applied to anything producing a structure or array, or the
output of an ordinary access procedure).
The value of the field is then whatever results from an access of
the type spec through the pointer produced by the underlying field
sub_spec, i.e. (treating the access code for spec as a 'procedure')
<access code for spec>( <underlying field pointer> ) -> <value>
Similarily, assigning to the field updates the type spec through the
pointer from the underlying field
<value> -> <access code for spec>( <underlying field pointer> )
(thus the field is updateable if and only if type spec is, etc).
Note, however, that a special case arises when spec is a compound
type (i.e. array, structure or function). Since in these cases an
access for spec gives the address of the field, this is just the
same as returning the underlying field pointer (i.e. the access code
for spec does nothing, and the value is the same as sub_spec alone).
The whole field specifier is therefore treated as if it WERE just
sub_spec, i.e. spec is simply taken to be typing information on the
external pointer field given by sub_spec, and is otherwise ignored.
Consequently, a field of this type is updateable (where a direct
compound type in a structure or array would not be), since updating
the address of the object is the same as updating the external
pointer field sub_spec.
Pointer Value as Data
This specifier enables the pointer value of an external pointer to
be accessed as data in its own right. It consists of a reference of
the form
consref(data_spec)
where data_spec is restricted to being a packed integer spec
("word", "int", etc), "sfloat" or "full".
This specifier may only appear as the direct argument to
cons_access, or as the spec of an implicit type access; its effect
is to `back off' by one level of pointer, and then access or update
through a pointer to the pointer value according to data_spec. (Note
that specifying a type smaller than a pointer is taken to mean the
low-order part of the value, e.g. "byte" returns the
least-significant byte of the pointer value.)
-----------------
10 Miscellaneous
-----------------
field_spec_info(spec) -> (value_spec, bitsize) [procedure]
Given any field type specifier spec (EXCEPT an external
function, i.e. a vector), returns the number of bits bitsize
occupied by the field; this is false for an unsized structure or
array. The value_spec result indicates what the field value will
be when accessed.
If spec contains any implicit access or conversion procedures,
then value_spec is false (since the system can't determine what
the field will produce).
If spec is for a compound type (i.e. structure list or array
pair) then value_spec = spec (which means "exptr" as far as the
field value is concerned, but spec is returned to indicate a
non-updateable pointer).
Otherwise, value_spec is as follows:
value_spec Meaning
---------- -------
"full" Any Poplog item
N Unsigned integer 0 <= I < 2**
-N Signed integer -2**(N-1) <= I < 2**(N-1)
"decimal" Decimal
"ddecimal" Ddecimal (or decimal, see * popdprecision)
"exptr" External pointer
"exval" External pointer or value
Note in particular that the value_spec returned for "pint" is
-(number of bits in a simple integer).
key_key -> key [constant]
The constant key structure for key structures themselves.
+-+ C.all/ref/keys
+-+ Copyright University of Sussex 1995. All rights reserved.