10. Constants

  1. 10.1. _cond constructors
  2. 10.2. Primitive constant constructors

The representation of constants clearly has peculiar difficulties in any architecture neutral format. Leaving aside any problems of how numbers are to be represented, we also have the situation where a "constant" can have different values on different platforms. An obvious example would be the size of a structure which, although it is a constant of any particular run of a program, may have different values on different machines. Further, this constant is in general the result of some computation involving the sizes of its components which are not known until the platform is chosen. In TDF, sizes are always derived from some EXP OFFSET constructed using the various OFFSET arithmetic operations on primitives like shape_offset and offset_zero. Most such EXP OFFSETs produced are in fact constants of the platform; they include field displacements of structure as well as their sizes. TDF assumes that, if these EXPs can be evaluated at translate-time (i.e. when the sizes and alignments of primitive objects are known), then they must be evaluated there. An example of why this is so arises in make_compound; the SHAPE of its result EXP depends on its arg1 EXP OFFSET parameter and all SHAPEs must be translate-time values.

An initialisation of a TAGDEF is a constant in this sense; [g] this allows one to ignore any difficulties about their order of evaluation in the UNIT and consequently the order of evaluation of UNITs. Once again all the EXPs which are initialisations must be evaluated before the program is run; this obviously includes any make_proc or make_general_proc. . The limitation on an initialisation EXP to ensure this is basically that one cannot take the contents of a variable declared outside the EXP after all tokens and conditional evaluation is taken into account. In other words, each TDF translator effectively has an TDF interpreter which can do evaluation of expressions (including conditionals etc.) involving only constants such as numbers, sizes and addresses of globals. This corresponds very roughly to the kind of initialisations of globals that are permissible in C; for a more precise definition, see ().

10.1. _cond constructors

Another place where translate-time evaluation of constants is mandated is in the various _cond constructors which give a kind of "conditional compilation" facility; every SORT which has a SORTNAME, other that TAG, TOKEN and LABEL, has one of these constructors e.g. exp_cond:

control: EXP INTEGER(v)
e1:      BITSTREAM EXP x
e2:      BITSTREAM EXP y
         -> EXP x or EXP y

The constant, control, is evaluated at translate time. If it is not zero the entire construction is replaced by the EXP in e1; otherwise it is replaced by the one in e2. In either case, the other BITSTREAM is totally ignored; it even does not need to be sensible TDF. This kind of construction is use extensively in C pre-processing directives e.g.:

#if (sizeof(int) == sizeof(long)) ...

10.2. Primitive constant constructors

Integer constants are constructed using make_int:

v:     VARIETY
value: SIGNED_NAT
       -> EXP INTEGER(v)

The SIGNED_NAT value is an encoding of the binary value required for the integer; this value must lie within the limits given by v. I have been rather slip-shod in writing down examples of integer constants earlier in this document; where I have written 1 as an integer EXP, for example, I should have written make_int(v, 1) where v is some appropriate VARIETY.

Constants for both floats and strings use STRINGs. A constant string is just an particular example of make_nof_int:

v:   VARIETY
str: STRING(k, n)
     -> EXP NOF(n, INTEGER(v))

Each unsigned integer in str must lie in the variety v and the result is the constant array whose elements are the integers considered to be of VARIETY v. An ASCII-C constant string might have v = variety(-128,127) and k = 7; however, make_nof_int can be used to make strings of any INTEGER VARIETY; a the elements of a Unicode string would be integers of size 16 bits.

A floating constant uses a STRING which contains the ASCII characters of a expansion of the number to some base in make_floating:

f:        FLOATING_VARIETY
rm:       ROUNDING_MODE
sign:     BOOL
mantissa: STRING(k, n)
base:     NAT
exponent: SIGNED_NAT
          -> EXP FLOATING(f)

For a normal floating point number, each integer in mantissa is either the ASCII `.'-symbol or the ASCII representation of a digit of the representation in the given base; i.e. if c is the ASCII symbol, the digit value is c-'0'. The resulting floating point number has SHAPE FLOATING(f) and value mantissa * base exponent rounded according to rm. Usually the base will be 10 (sometimes 2) and the rounding mode to_nearest. Any floating-point evaluation of expressions done at translate-time will be done to an accuracy greater that implied by the FLOATING_VARIETY involved, so that floating constants will be as accurate as the platform permits.

The make_floating construct does not apply apply to a complex FLOATING_VARIETY f; to construct a complex constant use make_complex with two make_floating arguments.

Constants are also provided to give unique null values for pointers, label values and procs i.e.: make_null_ptr, make_null_local_lv and make_null_proc. Any significant use of these values (e.g. taking the contents of a null pointer) is undefined, but they can be assigned and used in tests in the normal way.

  1. [g]

    However see also initial_value in section 3.2.