Imp
Copyright (c) 1998-1999 Redshift Software Inc. All Rights Reserved

3

Declarations

 
 
A declaration introduces identifiers and specifies their function . Declarations can create a named lexical scope, a routine, or a variable. All declarations have the following form:

identifier : declaration information

3.1
Types
 
 
IMP has only two basic types: the bit type which takes value 0 or 1, and the register type which takes unsigned integer values from 0 to 2**N where N is the machine dependent size of a register in bits. All other types are derived types, even such ordinary types as integer. IMP does, however, have the internal types of number, character, and logical to allow the programmer to convert to and from constant values to derived types. At compile time an appropriately defined coerce routine converts from the internal types to an actual (derived) type. As an example, consider the hypothetical derived type ascii. When a string constant is assigned to a variable of type ascii the string is coerced from the internal array of characters to the ascii representation of that string.

3.1.1
Internal types
 
 
Internal types do not define how much storage they have or how they are represented in memory. Instead, when assigned to a real type they are coerced into an appropriate representation and amount of storage. The internal types exist to make it straightforward to have constants in a program.

3.1.2
Derived types
 
 
Operators applied in declarations derive a new type from an existing type. The deriving operators are:

& pointer to
[] an array of
routine()() a routine accepting some arguments and returning some values
record {member1;member2;...membern} a record with some slots
These operators bind to the name of each identifier in a declaration or definition. Some examples are:

ptr: &integer; A pointer to an integer.
byte: [8]bit; An array of 8 bits.
pow: routine () (&float) {}; A routine returning a pointer to float.
info: record { name: [32]ascii_char; address: [128]ascii_char}; A record suitable for an address book.

3.1.3
Declaring New Types
 
 
New Types may be given names declaring them using the type keyword. As an example consider the following declaration of a byte type:
byte: type [8]bit
{
+: operator(a: byte;b: byte) (r: byte) { ... }
*: operator(a: byte;b: byte) (r: byte) { ... }
...
}
The ellipsis represent code elided to keep the example short. The new type defines storage and operations on the type. Relational operators must return a value of the internal type logical. IMP allows the programmer to define the behavior of every operator except &, @, [], cast, and size. The programmer may define operators for ~, `, !, $, ^, |, and \ in addition to the usual arithemetic and relational operators. Also the programmer may combine two operator tokens to create a new operator token (e.g. ++)

3.1.4
Enumerated Types
 
 
The form of an enumerated type declaration is as follows:
enum TypeDefinition (identifier1 ;identifer2 ;...indentifiern)
-> ( value1 ;value2 ;...valuen )
This defines an enumeration that maps the identifiers to the values of the specified type. The prototypical example is, of course, boolean:
boolean: type enum bit (false;true) -> (0;1);

3.1.5
Coerce and Cast
 
 
IMP will implicitly coerce one type into another during an assignment if a coerce routine exists to transform one type into the other. Coerce can also convert a list of values into structured data and vice versa. As an example, coerce could be used to fill an array:
myarray: [4]bit;
myarray <- coerce(1,0,1,0);
IMP can also explicitly cast one type into another, but this will only change the type and therefore may give meaningless results if used carelessly.

3.2
Scope
 
 
Identifiers within a program have lexical scope. A qualified identifier allows access to an identifier not in the current scope. Routine declarations create an implicit scope, but routine variables may not be exported. Declarations outside any lexical scope share a global scope. Export declarations specify which identifiers may be accessed from outside their own scope. Import declarations make exported identifiers visible within a scope.

3.3
Variables
 
 
A variable declaration has the following form:
identifierlist : typedefinition [<- valuelist]
This declaration creates a list of one or more variables that have the specified type. The optional assignment initializes the values of these variables.

3.4
Routines
 
 
A routine is a basic unit of computation. A routine takes some number of inputs, does some computation and returns some number of values as a result. A routine is the more general form of procedure and function calls. In IMP a routine declaration would look like this:
convolve: routine (m: [][]integer;v: []integer) (rm: [][]integer;ev: Error)
{
## do the convolution
## set the output variables
rm <- convolvedmatrix;
ev <- NOERROR;
};

 
Reference
 
1 Lexical
2 Programs
3 Declarations
4 Expressions
5 Statements
6 Preprocessor
7 EBNF