IMP80 Language Manual
To use the Index, type "index," followed by the entry you require
followed by '?'.  E.g. index,record?
Type C to get the full contents list.  Abridged contents:
PREFACE TO MANUAL                                          /2
HISTORICAL INTRODUCTION                                    /5
ELEMENTS OF THE LANGUAGE                                   1
TYPES, VARIABLES, CONSTANTS AND EXPRESSIONS                2
BLOCKS AND PROCEDURES                                      3
EXECUTABLE STATEMENTS                                      4
INPUT/OUTPUT FACILITIES                                    5
STORE MAPPING                                              6
STANDARD PROCEDURES                                        7
EXAMPLES (TO FOLLOW)                                       8
APPENDIX A: IMP80 SYNTAX                                   9
APPENDIX B: IMPLEMENTATION-SPECIFIC INFORMATION           10
APPENDIX C: EMAS - IMP80 CONVERSION                       11
INDEX                                                     12
....  Preface to manual follows ....
PREFACE
This manual describes the programming language IMP80, which is a common
subset of several extant versions of IMP.  The reason for the existence
of these different versions is explained in the "Historical
Introduction" (below).
It is intended that for the foreseeable future implementations of IMP
will follow IMP80 as far as possible, and in particular will not depart
from it for frivolous or cosmetic reasons; however, this does not
preclude implementation-specific extensions.
Separate sections of Appendix B will be provided for each major
implementation of IMP80.  These detail departures from or extensions to
IMP80, and include relevant system-dependent information.  Such
system-dependent information is also included occasionally within the
main body of the manual, where it was felt that to have excluded it
would have been inconvenient or even misleading.  Such material is
clearly flagged as not describing IMP80 itself, and is always repeated
in the relevant section(s) of Appendix B.
There are two implementations of IMP80 at the time of writing: EMAS
IMP80, implemented on the ICL 2900 range by Peter Stephens, ERCC; and
IMP77, implemented on several different machines by Peter Robertson,
Lattice Logic Ltd., Edinburgh.
This is primarily a reference manual, and it is hoped that the contents
list and index are detailed enough to enable it to be used as such.  In
addition, however, it is felt that guidance on the use of language
features should be given, as their utility might not be apparent from a
statement of their syntax and semantics alone.  The programming examples
included for illustrative purposes are also intended to indicate good
programming practice.  Furthermore, Section 8 includes advice on writing
IMP80 programs as well as some larger examples.
A Backus-Naur Form (simplified) specification of the complete syntax of
IMP80 is given in Appendix A.  An explanation of the conventions used in
the syntax definition is given at the end of Appendix A.
This manual was written by Felicity Stephens and John Murison, apart
from the Historical Introduction by Peter Stephens.  Much of the text is
based on earlier documents, in particular: "The Edinburgh IMP Language
Manual" (second edition) edited by Roderick McLeod, Edinburgh Regional
Computing Centre (1974), and "The IMP-77 Language" (third edition) by
Peter Robertson, Department of Computer Science, University of Edinburgh
(1980).
Please report any suspected errors or omissions in this manual to the
ERCC Advisory Service, James Clerk Maxwell Building, The King's
Buildings, Mayfield Road, Edinburgh EH9 3JZ.
                                                         John M. Murison
                                                                  Editor
                                                            October 1981
HISTORICAL INTRODUCTION
Evolution
   The Imp language is evolved from Atlas Autocode, which itself is a
direct descendant of Algol 60.  Although Algol 60 had only moderate
success as a programming language - it was hardly used in the United
States - no other language before or since has achieved more than a
fraction of its influence on programming language design.
   At the same time as Algol was being devised and revised, in
Manchester another event was taking place which was also to have wide
influence.  The University was building its fourth machine (the Atlas) -
which true to tradition was at the very limit of the technology of the
time.  This machine was to introduce paging to the world; the idea being
that memory management, provided by the operating system with hardware
support, was cheaper and more efficient than allowing each programmer to
overlay or shoehorn his program into the space available.  After a slow
start, this idea was to change the appearance of computing.  Since Atlas
was a revolutionary machine, Manchester had to write their own software,
as they had done for the Manchester Mark 1 and Mercury.  This led them
to consider the attractions and disadvantages of Algol.
   Algol's principal attraction is its block and stack structure: by
collecting space together on a stack and re-using it for successive
procedure calls, an Algol program causes much less paging than the same
program written in (say) Fortran.
   The disadvantages of Algol are the lack of standard input/output and
the difficulties that some features of the language present to the
compiler writer.  The tragedy of Algol was that so little was gained
from the features which presented most of the problems.  Almost nothing
of real power was gained either from call by substitution or failure to
specify formal procedures adequately, and little was gained by the
enormous generality of the %for statements.  Yet the problems these
areas posed caused all early Algol compilers to produce comparatively
low quality object code.
   All this and more was obvious to the Manchester team and although
Algol was to be implemented on Atlas, the prime language was a new one -
Atlas Autocode.  This was a simplified Algol with changes to the block,
loop and procedure structures to remove the worst problem areas.  It
contrived to deliver 90% of the power of Algol to the programmer while
only requiring 25% as much effort from the compiler writer.  (Further
details can be obtained from The Computer Journal, Vol 8, pp 303-310
(1965/66)).  In retrospect, the name was unfortunate since autocodes
were normally low level languages, and "Atlas" indicated quite wrongly a
degree of machine dependence.
   Edinburgh University started its computing with a data link to the
Manchester Atlas and this happy accident began the long association
between the University and the language.  When Glasgow and later
Edinburgh obtained KDF9 computers it was necessary to write a compiler
for Atlas Autocode; this was carried out in a short time by Mr (now
Professor) H. Whitfield and his associates.  This compiler was in
advance of its time in that it was written entirely in Atlas Autocode
and developed on Atlas.  It was transferred to KDF9 by the elegant
technique of self-compilation.  The compiler thus produced compared
exceedingly well with the manufacturer's Algol compiler, both in
compilation time and in object code efficiency.  This project also
confirmed that Atlas Autocode was free of implementation trouble spots
and very suitable for large scale system programming.
   In 1966 there began a large scale project with a joint
University/Manufacturer team to write a time-sharing operating system
for the ICL 4-75 computer.  The project was based in Edinburgh and the
final system would have to support Atlas Autocode among other languages.
The recent success of the Atlas Autocode compiler project led to the
decision to implement the time-sharing system (later called EMAS) in a
high level language called IMP.  IMP was to be a superset of Atlas
Autocode, containing additional features for system programming.  It was
at this point that almost all the main language changes were made and
the distinctive philosophy of IMP originated.
Philosophy and Style
   IMP was to be primarily a system programming language; in 1966 that
was perceived to require:
   * efficient object code.  System programs are liable to be executed
     millions of times.  Thus features that could not be implemented
     efficiently should be omitted.
   * early compiler availability.  The compiler should be available as
     soon as the hardware, otherwise programmers would program in
     something else.  Consequently features that would or might cause
     implementation trouble spots must not creep into the language.
   * minimum run-time support.  Some system programs like supervisors
     and loaders have to run in an environment almost devoid of support
     software.  The language should be free of features requiring
     run-time support.  (The Atlas Autocode fault statement conflicted
     with this aim and had to be banned from system programs, although
     retained in the language for user programs.)
   * readability.  System programs have a long life and require
     maintenance.  It is more important that the program be easy to
     understand than quick to write.  Optional keyword omission or
     abbreviations should be banned.  (The language should be "verbose
     rather than obscure".)
   * access to bizarre hardware features.  System programs require
     access to funny features - to blow the hooter or ring the gong on
     hardware failure, for example.  However the language would not be
     compromised here.  Instead machine coding would be allowed at any
     point in the program, with access to the IMP variables and arrays.
   Even with fifteen years of hindsight this list still seems very
relevant, although the need for efficient object code was more pressing
then than now.  The real key to the long life of IMP is the last item in
the list.  By allowing machine code in extremis almost no machine
dependent features were included except the underlying one of a byte
address structure.  Consequently IMP has been successful on a dozen or
so machines, unlike the main competitors of the era (PL360, Burroughs
Extended Algol).
   In accordance with the language philosophy, the following changes
were made to Atlas Autocode to produce IMP:
   * The internal character code was changed to ISO.
   * Logical operations were added to the language.
   * The additional declarators %byte %integer and %short %integer were
     introduced.
   * Structured data objects - %records - were introduced.
   * Text handling features were added specifically to aid writing of
     command interpreters.
   * Pointer variables and additional features were added to enable
     programmers to operate on storage areas outwith the compiler
     controlled stack.
   This language was used in the writing of EMAS and it proved possible
to write a high performance multi-access operating system almost
entirely in IMP.  (Those requiring further information should read The
Computer Journal, Vol 17, pp 216-223 (1974).)
Recent Developments
   After the successful completion of EMAS, responsibility for
maintenance of the system and the IMP compiler passed from the
department of Computer Science to the Edinburgh Regional Computing
Centre.  Over the next ten years the language evolved very slowly - even
a move of EMAS from ICL 4-75 to ICL 2900 hardware scarcely disturbed the
stability of the language.
   However, the Computer Science department, in pursuing its diverse
research interests, encountered a variety of machines and wrote IMP
compilers for a substantial proportion of them.  As befits an academic
department these compilers contained novel features and gradually
diverged from EMAS IMP and from each other.  In 1980 a stock-taking was
instituted, from which there gradually emerged the common core of
features described in this manual.  Most compilers will have some
additional features but will support this common base, and it should be
possible to write most programs in the common subset.  Such programs
should be readily portable in the Edinburgh environment.
                                                           P.D. Stephens
Elements of the language
IMP80 is a high-level block-structured language.  Relative to Algol 60,
it adds program structuring, data structuring, event signalling and text
handling facilities, but removes (or retains in a modified form)
intrinsically inefficient features such as the Algol 60 %name-type
parameter.
Character set
An IMP80 program is a sequence of statements constructed using the ISO
seven bit character set extended with a special alphabet.  The special
alphabet is used to construct keywords.
The ISO seven bit character set is as follows:
_______________________________________________________________________
|               |                    |                 |              |
|  0    NUL     |     32    SPACE    |    64    @      |      96    ` |
|  1    SOH     |     33    !        |    65    A      |      97    a |
|  2    STX     |     34    "        |    66    B      |      98    b |
|  3    ETX     |     35    #        |    67    C      |      99    c |
|  4    EOT     |     36    $        |    68    D      |     100    d |
|  5    ENQ     |     37    %        |    69    E      |     101    e |
|  6    ACK     |     38    &        |    70    F      |     102    f |
|  7    BEL     |     39    '        |    71    G      |     103    g |
|  8    BS      |     40    (        |    72    H      |     104    h |
|  9    HT      |     41    )        |    73    I      |     105    i |
| 10    LF(NL)  |     42    *        |    74    J      |     106    j |
| 11    VT      |     43    +        |    75    K      |     107    k |
| 12    FF      |     44    ,        |    76    L      |     108    l |
| 13    CR      |     45    -        |    77    M      |     109    m |
| 14    SO      |     46    .        |    78    N      |     110    n |
| 15    SI      |     47    /        |    79    O      |     111    o |
| 16    DLE     |     48    0        |    80    P      |     112    p |
| 17    DC1     |     49    1        |    81    Q      |     113    q |
| 18    DC2     |     50    2        |    82    R      |     114    r |
| 19    DC3     |     51    3        |    83    S      |     115    s |
| 20    DC4     |     52    4        |    84    T      |     116    t |
| 21    NAK     |     53    5        |    85    U      |     117    u |
| 22    SYN     |     54    6        |    86    V      |     118    v |
| 23    ETB     |     55    7        |    87    W      |     119    w |
| 24    CAN     |     56    8        |    88    X      |     120    x |
| 25    EM      |     57    9        |    89    Y      |     121    y |
| 26    SUB     |     58    :        |    90    Z      |     122    z |
| 27    ESC     |     59    ;        |    91    [      |     123    { |
| 28    FS      |     60    <        |    92    \      |     124    | |
| 29    GS      |     61    =        |    93    ]      |     125    } |
| 30    RS      |     62    >        |    94    ^      |     126    ~ |
| 31    US      |     63    ?        |    95    _      |     127   DEL|
|_______________|____________________|_________________|______________|
The use of the character set is summarised in the sections below and
described in detail in the rest of the manual.  However, some general
points about certain characters should be noted at the outset:
a) Quotes
  Several language constructions call for one or more characters to be
  enclosed in quotes.  Within quotes all characters are significant and
  stand for themselves; thus, for example, space, newline, and percent
  characters may appear between quotes and stand for space, newline, and
  percent.
  Two quote characters are used:
              '        -  character quote
              "        -  string quote
  Examples:
              'A', ';', '"', '!'
              "String", "sealing wax", "!"
  If it is required to include the delimiting quote within the text it
  must be represented by two consecutive quotes.
  Examples:
              ''''                   -  the character quote
              "A ""big"" dog"        -  a string of eleven characters
  However, note '"' and "it's mine".
b) Spaces
  Except when used to terminate keywords or when between quotes, space
  characters are ignored and may be used to improve the legibility of
  the program.
c) Lower case letters
  Except when enclosed in quotes, lower case letters are equivalent to
  the corresponding upper case letters.
d) Non-graphic characters
  The ISO characters with no graphic representation have code values
  less than 32 or greater than 126.  Any non-graphic characters can be
  included within quotes in an IMP80 program, but only one, the newline
  character (NL), is included in the syntactic definition of the
  language (see Appendix A).  This has the ISO code value 10.
  {EMAS IMP80: all characters with code values less than 32 are treated
  as spaces, apart from
        NL (ISO code 10)
        EM (ISO code 25)  - terminates program
        SUB(ISO code 26)  - causes a syntax fault
  It ignores DEL (ISO code 127) and maps all codes greater than 127
  according to the extended Regional Network Code (which is detailed in
  Regional Communications Memo JID/78/M20.4). }
  {IMP77
  Except for NL (see above) all characters not enclosed in quotes and
  with ISO codes outwith the range 32 to 126 inclusive are treated as
  spaces, but will be sent to the listing unaltered.  The character FF
  (form feed) may be used to cause a new page to be taken in program
  listing files.}
Statements
An IMP80 program typically consists of an ordered set of statements, as
follows:
        %begin
           <declaration statements
           and procedure descriptions>
           %on %event .... %start
              <executable statements>
           %finish
           <executable statements>
           <procedure descriptions>
        %end %of %program
Only the delimiting keywords %begin and %end %of %program must be
present.  However, assuming that all the types of statement above are
represented in an IMP80 program, the effect of running the program on a
computer is as follows:
  First, storage space for the declarations is obtained.  Conceptually,
  all the space is obtained at once - there is no defined order of
  execution of declaration statements.  The procedure descriptions are
  skipped, as are the statements in the %on %event block, and the first
  of the executable statements is obeyed.  Thereafter, the statements
  themselves define the sequence of execution ("flow of control").
  Control passes to the %on %event block if one of the events defined in
  the %on %event statement occurs whilst executing a statement.
  The %on %event block is optional and often omitted.
Here is an IMP80 program:
        %begin
           %integer A, B, SUM, DIFFERENCE, MAX, MIN
           MAX = -1; MIN = 5000
           %cycle
              READ(A)
              %exit %if A=-1; ! End of input.
              READ(B)
              PRINTSTRING("Input values:")
              WRITE(A,3); PRINTSYMBOL(',')
              WRITE(B,3)
              SUM = A+B; DIFFERENCE = A-B
              PRINTSTRING(" ... Sum is")
              WRITE(SUM,3)
              PRINTSTRING(",   Difference is")
              WRITE(DIFFERENCE,3); NEWLINE
              %if SUM>MAX %start
                 MAX = SUM
              %finish %else %if SUM<MIN %start
                 MIN = SUM
              %finish
           %repeat
           ! Now print out the maximum and minimum of the sums of all
           ! the pairs of numbers read in.
           NEWLINE
           PRINTSTRING("Maximum sum is")
           WRITE(MAX,3); NEWLINE
           PRINTSTRING("Minimum sum is")
           WRITE(MIN,3); NEWLINE
        %end %of %program
The different types of statement in IMP80 are listed below, with
references:
           %comment                   1.2.3.1
           %include                   1.2.3.2
           %list & %end %of %list     1.2.3.3
           declaration                2
           assignment                 2
           %begin                     3.1
           %end %of %program          3.1
           %on %event                 3.1.1
           procedure specification    3.2/5
           procedure heading          3.2
           procedure call             3.2
           %end %of %file             3.3.1
           jump & label               4.2.2
           %start/%finish             4.3
           %cycle/%repeat             4.4
Continuation
  Statements are normally terminated by a newline or semi-colon
  character.  However, a statement may extend over several physical
  lines provided that each line break occurs after a comma, or is
  preceded by the keyword %c (which is otherwise ignored).
  Thus:
           %if X=Y %then P = 1 %c
                  %else P = 0
  is exactly equivalent to
           %if X=Y %then P =1 %else P = 0
  and
           %own %integer %array X(1:10) = 10, 9, 8, 7, 6,
                                          5, 4, 3, 2, 1
  is exactly equivalent to
           %own %integer %array X(1:10) = 10,9,8,7,6,5,4,3,2,1
  {EMAS IMP80: Blank lines between the lines of a continued statement
  are ignored.}
  {IMP77: the line break can also occur after the keywords %or and
  %and.}
Statement components
IMP80 statements are made up of atoms.  An atom is an identifier, a
constant, a special symbol or a keyword.
Identifiers
  An identifier is a sequence of any number of letters and digits,
  starting with a letter.  For example: C1900 TO 1970, NUMBER OF BLOCKS,
  MAX, x, item1, Item 2, item 2b.  Spaces within identifiers are
  permitted but ignored, as is the case of the letters; thus Item 2 and
  ITEM2 are not distinguished.
  It is recommended that meaningful identifiers be used whenever
  possible, to improve the clarity of programs.
  In this manual, identifiers are always given in upper case.  It is
  stressed that this is merely a convention.
  Identifiers are used to name the following entities:
        Arithmetic, string and record variables and arrays
        Reference variables
        Record sub-fields
        Procedures (i.e. routines, functions, maps)
        Record and array formats
        Named constants
        Simple labels and switch vectors
  Identifiers used in an IMP80 program must be declared before they may
  be used.  The only exception to this general rule is that simple
  labels are not declared.  Further rules relating to the uniqueness of
  identifiers are given in the relevant sections.
Constants
  IMP80 includes the following types of constant:
     Decimal integer constants        e.g. 2243, -16, 1 000 000
     Base constants                   e.g. 2_1001, 8_7720, 16_A06C,
                                           B'1011', X'1A69'
     Real constants                   e.g. 120.0, 120, 1.2@2, 12@1,
                                           1200@-1
     Character constants              e.g. 'A', 'a', '+', '"', '
                                           ', '''', '6'
     Multi-character constants        e.g. M'Four', M'MAX', M'1+1=',
                                           M'"#%'
     String constants                 e.g. "Here is a string", "A",
                                           "123", "a ""good"" boy"
     Named constants (also called     e.g. %constant %integer PRICE=23
     "%constant variables")                 :
                                           TOTAL COST = PRICE * NUMBER
Special symbols
  These are as follows:
   _________________________________________________________________
  |                                                                 |
  |  !           !!          '           "           # (or \= or <>)|
  |                                                                 |
  |  ## (or \==) &           (           )           *              |
  |                                                                 |
  |  \           \\          +           ,           -              |
  |                                                                 |
  |  ->          .           /           //          :              |
  |                                                                 |
  |  ;           <           <-          <<          <=             |
  |                                                                 |
  |  =           ==          >           >=          >>             |
  |                                                                 |
  |  @           \ (or ~)    _           {           }              |
  |                                                                 |
  |  newline     space                                              |
  |_________________________________________________________________|
  {IMP77: \ and \\ may be replaced by ^ and ^^ respectively.}
  {EMAS IMP80: \ and \\ may be replaced by ** and **** respectively.}
  The special symbols are used in IMP80 in various ways:
   * as operators (arithmetic, string, record, relational, logical)
   * as separators, e.g. (..) {..} : ; , newline space
   * in constants, e.g. 3_201, +27@4, -17.6, X'8F', "Constant"
   * in record subfield references, e.g. TAX(MONTH)_OVERTIME
   * ! as an alternative to the keyword %comment
Keywords
  A keyword is a sequence of letters in a special alphabet.  In programs
  stored in a computer in text form ("source" programs), representation
  of this alphabet is achieved by using the character percent (%), which
  is defined as causing the subsequent letters (upper or lower case) to
  be shifted into the special alphabet.  The effect of the % character
  is terminated by any non-alphabetic character, including space and
  newline.  Hence the following statements are equivalent:
        %string(7) %array %name P
        %STRING (7) %ARRAYNAME P
  In this manual keywords are given in lower case.
  The following is a list of all the IMP80 keywords:
      ____________________________________________________________
     |                                                            |
     |  %alias      %and        %array      %begin      %byte     |
     |  %c          %comment    %const      %constant   %continue |
     |  %cycle      %dynamic    %end        %event      %exit     |
     |  %external   %file       %finish     %fn         %for      |
     |  %format     %function   %half       %if         %include  |
     |  %integer    %list       %long       %map        %monitor  |
     |  %name       %not        %on         %of         %or       |
     |  %own        %program    %real       %record     %repeat   |
     |  %result     %return     %routine    %short      %signal   |
     |  %spec       %start      %stop       %string     %switch   |
     |  %system     %then       %unless     %until      %while    |
     |____________________________________________________________|
Miscellaneous statements
Certain statements are described here rather than in subsequent
sections, as they are different in kind from the other statements of the
language, and do not readily fit into any of the categories used in the
rest of the manual.
Comments
  Textual comments can be included in an IMP80 program.  They have no
  effect on the program when it runs, but may be used to render it
  meaningful both to the originator and to others.  A comment is a
  statement, and must be separated from the preceding statement by a
  newline or semi-colon and from the succeeding statement by a newline -
  not by a semi-colon.  Note that the continuation rules described above
  apply to comment statements.  The keyword %comment is used to
  introduce the text of the comment, which may contain any character
  except newline.  %comment may be replaced by the symbol '!'.
  {IMP77: comments cannot be continued onto subsequent lines by any of
  the methods described.}
  Example:
           %begin
              %comment This program sorts a list of names
              %comment into alphabetical order
               :
               :
           %end %of %program
  The IMP80 language also allows comments to be embedded within other
  statements.  An embedded comment is any sequence of characters,
  excluding '}' and newline, enclosed in a pair of braces, '{' and '}'.
  This form of comment may appear between any two atoms, but may not
  occur within an atom.  For convenience the closing brace may be
  replaced by a newline.
  Example:
           ! This is a portion of a test program
           COUNT = 0; ! Note the semi-colon before the '!'; note thenext one
                      ! next one (in the text of the comment).
           LIMIT = 100      {Only 100 cases}
           MINIMUM = 0      {all positive
           PROCESS(X {cases}, Y {total cost})
        This fragment of program is exactly equivalent to
           COUNT = 0
           LIMIT = 100
           MINIMUM = 0
           PROCESS(X,Y)
  Note that this type of comment never counts as a statement in its own
  right, even when given on a line by itself.  Thus it can be interposed
  between lines of a continued statement.
%include statement
  A file of IMP80 statements (terminated by the statement %end %of
  %file) may be included in a source program by giving a statement of
  the form
           %include <file specification>
  where <file specification> is a string constant representing a
  system-dependent file name.  Refer to the relevant section of
  Appendix B for details of any implementation-dependent limitations on
  the use of %include.
  Example:
           %begin
              %include "WXYZ83.EXTSPECS"
              %integer TIME, DISPLACEMENT, ...
               :
               :
  Note that the %include statement appears in the program listing
  generated by the compiler, followed by the contents of the file
  referred to (including the %end %of %file).  The %include statement
  should not be followed on the same line of the source program by other
  statements, since the complete source line will appear in the program
  listing before the statements included, while in the actual program
  the statements following the %include follow the statements included.
%list and %end %of %list
  Normally the compiler generates a listing of a program as it compiles
  it.  The precise layout of this listing is implementation-dependent.
  The production of the listing can normally by controlled by setting a
  system option prior to the compilation.  In addition, however, the
  programmer can control the generation of the listing by use of the
  statement %end %of %list, which inhibits the listing until the end of
  the program or the statement %list is encountered.  The default is for
  the listing to be generated.
  The statements %end %of %list and %list may appear anywhere within an
  IMP80 program.
  Note that when listing is inhibited, incorrect statements and the
  accompanying fault messages are still listed.
  {IMP77: %end %of %list and %list are nested, so that two %end %of
  %list statements require two matching %list statements to switch on
  the listing again.  This means that it is possible within an included
  file (see above) to control the listing of the contents of the file,
  without changing the listing status of the rest of the program.}
Variables, constants, expressions
Types
Each data item (i.e. each constant or variable) used by an IMP80 program
has a type associated with it which determines what sort of item it is
or can have as a value.
There are five categories of type:
Arithmetic types
  These comprise integer and real types, as follows:
        %byte %integer
        %short %integer
        %half %integer
        %integer
        %long %integer
        %real
        %long %real
        %long %long %real
  Some implementations of IMP80 might not provide all of these types;
  see Appendix B for details.
  The modifiers %byte, %short, etc. relate to the size or precision of
  items of the appropriate type.  For byte-addressed machines, the
  ranges and (where appropriate) precisions associated with arithmetic
  types are as follows:
 ______________________________________________________________________
|                                                                      |
| type              normal         range of values                     |
|                   storage        (inclusive)                         |
|                   allocation                                         |
|______________________________________________________________________|
|%byte %integer      8 bits         0 to 255                           |
|(or just %byte)                                                       |
|%short %integer    16 bits        -32767 to 32767                     |
|(or just %short)                                                      |
|______________________________________________________________________|
 ______________________________________________________________________
| type              normal         range of values            precision|
|                   storage        (inclusive)                (decimal |
|                   allocation                                 digits) |
|______________________________________________________________________|
|%half %integer     16 bits         0 to 65535                         |
|(or just %half)                                                       |
|%integer           16 bits or     -32767 to 32767 or                  |
|                   32 bits        -2147483647 to 2147483647           |
|%long %integer     64 bits        -9223372036854775807 to             |
|                                   9223372036854775807                |
|    Integer arithmetic involving values outside the range             |
|    which a variable of type %integer can hold may be invalid.        |
|______________________________________________________________________|
|%real              32 bits   \                                  7     |
|                              |   -7.2x10@75 to -1.2x10@(-77),        |
|%long %real        64 bits    |-   0,                           16    |
|                              |    1.2x10@(-77) to 7.2x10@75          |
|%long %long %real 128 bits   /                                  36    |
|______________________________________________________________________|
String type
  The type %string relates to sequences of characters.  An item of this
  type has a length associated with it, the actual or maximum number of
  characters which the item comprises or can have as a value.
Record types
  An item of type %record is a composite of several sub-items, each of
  which has an associated type.  When a variable of type %record is
  declared, its precise composition must be specified.
Array types
  An item of array type is a composite of (in general) several
  sub-items, each with the same associated type, which must be one of
  those listed above.  The name of the array type is obtained by
  appending the keyword %array to the name of the type in question.
  For example:
           type                 corresponding array type
           %byte %integer       %byte %integer %array
           %record (<format>)   %record (<format>) %array
           %string(<n>)         %string(<n>)%array
Reference types
  An item of this type is, or has as its value, a reference to a
  variable of a specified type.  For each of the foregoing types there
  is a corresponding reference type, the name of which is obtained by
  appending the keyword %name to the name of the type in question.
  For example:
           type                 corresponding reference type
           %byte %integer       %byte %integer %name
           %record (<format>)   %record (<format>) %name
           %string(<n>)         %string(<n>)%name
           %real %array         %real %array %name
  Variables of this type are known as reference variables or pointer
  variables.
Variables
Variables are named store locations used to hold numeric or textual
information.  Each variable must be defined in a declaration statement
which specifies its type and an identifier to name it.  The amount of
storage allocated to a variable depends on its type.  All variables must
be declared at the head of the block in which they are to be used, or in
an outer block.
The initial value of a variable, i.e. the value assigned to it when it
is created, is implementation-dependent; in most cases it is undefined
(see Appendix B for details).  Any attempt to use a variable whose value
is undefined will cause an event to be signalled.
{IMP77: all variables can be assigned initial values.  The syntax is as
for %own variable initialisation, described below.  Stack variables are
re-initialised whenever they are re-created.}
Variables can be divided into four categories:
           Arithmetic
           String
           Record
           Reference
The following standard integer function is provided (a 'standard'
procedure is one which is predefined; see Section 7):
  %integer %function SIZE OF(%name A)
     The number of storage units occupied by the given variable is
     returned.  The unit is implementation-dependent but commonly is a
     byte.
Arithmetic variables
  Arithmetic variables may be of two types: %integer and %real.  The
  first holds whole numbers and the second holds numbers with fractional
  parts.  For more efficient use of store, %short, %half, %byte and
  %long integer types may be provided, whilst greater precision will be
  obtained when %long %real and %long %long %real are available.
  However, the provision of different lengths is necessarily hardware
  dependent, though type %integer will always be available; it
  corresponds to the word length of the machine.  Furthermore, it is
  possible that on some restricted implementations for small machines,
  type %real will not be supported.  Where implementation is on a
  byte-addressed machine (e.g. IBM 370, ICL 2900 ranges) %short %integer
  and %half %integer would be 16 bits in length, %integer 16 or 32 bits,
  %long %integer 64 bits, and %real types would be 32 bits, 64 bits and
  128 bits in length.  On such machines, %real variables can only hold
  values to a precision of 7.2 significant decimal digits whereas %long
  %real variables are precise to 16.8 decimal digits and %long %long
  %real variables (if available) to 36 decimal digits.  Further details
  of the representation of variables can be found in the relevant
  hardware manuals.
  The following example illustrates the declaration of various
  arithmetic variables.
  Example:
           %begin
              %integer I, J
              %real P, Q
              %begin
                 %byte %integer Z
                  :
                  :
                 P = I+J+Q
                 Z = 0
                  :
                  :
              %end
               :
               :
           %end %of %program
  Arithmetic variables can be grouped into arrays.  The array bounds
  separated by the symbol ':' are given in brackets after the array
  identifier when the array is declared.
  Example:
           %integer %array IN(1:10), OUT(1:20)
  Multi-dimensional arrays can be used.  The maximum number of
  dimensions is implementation dependent.
  Example:
           %integer %array BITLIST(-4:4, 1:2, 10:100, 1:2)
  When an individual array element is accessed, the array identifier is
  followed by an ordered list of integer expressions (one for each
  dimension) enclosed in brackets.  These integer expressions are called
  subscripts.
  Examples:
           BITLIST(3, 1, 54, J)
           BITLIST(I+K, I, 10, 1)
  Each of these integer expressions must evaluate to an integer which
  lies within the range described by the bounds for the relevant
  dimension.  A run time fault may occur if the array bounds are
  exceeded.
  An array can be declared with integer variables instead of constants
  for the array bounds.  Obviously, the variables used in such a
  declaration must be declared and given a value before the array
  declaration occurs.
  Examples:
           %begin
              %integer TOP
              READ(TOP); ! TOP is given a value from the input data.
              %begin
                 %integer %array TABLE(1:TOP)
                  :
                  :
              %end
               :
           %end
String variables
  A string variable is one which holds textual information.  The maximum
  length of a string is implementation dependent but is not normally
  less than 255 characters.  When the string variable is declared, the
  maximum number of characters which the string may hold is specified,
  in parentheses, after the keyword %string.
  Example:
           %string(24) S
            :
           S = "Results of last test"
  As the string of characters may vary in length within the given
  location while the program executes, an indication of the current
  length is stored along with the current contents.
  In all implementations on byte-oriented machines to date, the current
  length is held in an extra byte at the front of the string location.
  Thus the string S declared in the above example would be allocated 25
  bytes of storage.
  As can be seen from the above example, the character " (double quote)
  is used to delimit textual information to be stored in a string
  location.  Where one double quote character is part of the text, two
  consecutive double quote characters should occur in the text, to
  distinguish it from the terminating delimiter.
  Example:
           MESSAGE = "Peter says ""No"" "
  Multi-dimensional string arrays can be used.  Note that the maximum
  number of dimensions is implementation dependent.  Each element of a
  string array must have the same maximum length, which is specified
  when the array is declared.
  Example:
           %string(63)%array FIELDS (1:5)
     FIELDS consists of five strings, each of maximum length 63
  characters.
  The occurrence in an IMP program of a semi-colon or newline character
  normally terminates a statement.  However, both are permitted in
  string constants.
  Example:
           S = "A; B; C"
           SNL = "
           "
  A string may be regarded as having a value based on the ISO code
  values of the characters of the string.  Thus the relational operators
  >, <, =, #, <=, >= may be used to compare strings.  In particular,
  strings composed entirely of alphabetic characters can be regarded as
  having a dictionary ordering for the purposes of comparison.
  Example:
           "AB" < "C"        is TRUE
           "AB" < "ABC"      is TRUE
           "IMP" < "FORTRAN" is FALSE
Record variables
  A record is a variable comprising a collection of entities which may
  be of different types.  It has an identifier which refers to the whole
  collection and each entity has an identifier; an entity (or
  "sub-field") can be referenced by using the record identifier together
  with the required sub-field identifier.
  The collection of sub-fields which makes up the record is described in
  a record format statement, which specifies the identifier and type of
  each sub-field.
  Arrays with constant bounds may be used as sub-fields.
  Example:
           %constant %integer FROM=3, TO=5
           %record %format F(%byte %integer A, %string(8) S,
                      %integer %array M, F(FROM:TO, 1:400), %real Y)
           %record (F) R
            :
  Record format statements are placed with other declarations at the
  head of a block or procedure.  They do not cause allocation of
  storage.
  Note that the sub-field identifiers need not be distinct from other
  identifiers in use in the program, as they are always associated with
  a specific record identifier.
  A record format may include alternatives.
  Example:
           %record %format AS(%byte %integer %array CHAR(0:12) %or %c
                              %string(12) TEXT)
     The space allocated to a record of this format can be regarded as
     holding a byte integer array of 13 elements or a string of maximum
     length 12 characters.
  Alternatives provide a means of imposing different interpretations on
  all or part of a record.  Where only part of a record is to have an
  alternative format, brackets must be used within the record format
  statement to enclose the alternatives.
  Example:
           %record %format AT(%real X,
                              (%byte %integer A, B, C  %or  %real R %c
                               %or  %integer E),
                              %string(10) F)
  Every sub-field in the record format must be distinct.  Each
  alternative will start at the same address within the record and will
  be padded out to the size of the largest.  (In the example above
  padding will be added, as the three alternatives are not all of the
  same size).  The amount of padding required depends on the amount of
  store allocated to each sub-field, which is implementation-dependent
  (as is the relation between elements in different alternatives).
  A record format may contain several sets of alternatives, and
  alternatives may be nested to any depth.  Note, however, that
  redundant brackets within a record format statement are not allowed.
  Records are declared at the head of a block or procedure.  Space is
  allocated according to the associated record format statement whose
  identifier occurs in parentheses in the record declaration.  The
  amount of space occupied by a record of given format is the total
  number of bytes occupied by all the sub-fields plus the minimum number
  of 'padding' bytes required to achieve alignment of sub-fields
  appropriate to their types.  This alignment is
  implementation-dependent.
  Example:
           %record %format F(%integer A, %string(8) S)
           %record (F) R
           %record (F) PP, QQ, RR
  Note that, as in the example above, more than one record may be
  declared having the same record format.
  {EMAS IMP80: in a record declaration it is permissible to give the
  actual record format instead of giving the name of a record format.
  Example:
           %record (%integer I, J, %string(7) S) A, B, C
  It is also permissible to give the name of a record already declared
  in place of a record format name.  The format of the specified record
  is then taken as the required format for the record being declared.
  Example:
           %record (A) D
           ! A is the record declared in the previous example.
  Each sub-field of a record can be referenced as a location of the
  appropriate variable type by subscripting the record identifier with
  the sub-field identifier, the two being separated by the underline
  character '_'.
  Examples:
           %record %format F(%integer A, %string(8) S)
           %record (F) R
            :
           %if R_S="INC" %then R_A = R_A+1
            :
           %record %format PE(%integer I, %real %array X(0:10))
           %record (PE) P
           %integer J
            :
           P_X(J+1) = P_X(J)*2
            :
            :
  Arrays of records are analogous to the arithmetic and string types of
  arrays already described.  Each element of a record array is a record
  of format specified in the record array declaration.
  Example:
           %record %format F(%integer A, %real %array X(1:5))
           %record (F) %array RA(1:100)
     The fifth element of sub-field X in the 76th record array element
     may be referenced as follows:
           RA(76)_X(5)
  As with other types of array, several record arrays having the same
  bounds and format may be defined in a single declaration.
  Example:
           %record (F) %array RR1, RR2(1:100)
  A sub-field may be of type %record.  In this case its format must have
  been already declared.  In particular, its format may not be the
  record format being described (c.f. record name sub-fields).
  Example:
           %record %format P(%integer %array X(0:4), %integer I)
           %record %format F1(%integer A, B, %record (P) D)
           %record %format F2(%record (P) J, K)
           %record (F1) ENT
           %record (F2) JAK
  An arbitrary depth of subscription can thus obtain.  Using the above
  declarations, the following are valid references to record elements:
           ENT_D_X(1)
           JAK_J_I
  A sub-field of type %record is word-aligned in most implementations,
  irrespective of the format of the sub-field.
  Whilst sub-fields of records may be used exactly like IMP80 entities
  of corresponding type, it is also possible to assign a whole record
  from one location to another.  Two assignment operators are permitted:
  '=' and '<-'.  Both operators require that operands on each side of
  the assignment refer to %record locations, except in the case where
  zero occurs on the right-hand side of the '=' operator: this results
  in the space allocated to the record referenced by the left-hand
  operand being set to binary zeros.
  When the '=' operator is used, the record formats associated with the
  left-hand and right-hand operands must be the same.
  {EMAS IMP80: the formats need not be the same, but they must have the
  same length.}
  The '<-' assignment operator transfers as many bytes from the record
  referenced by the right-hand operand as will fit into the record
  referenced by the left-hand operand.  The '<-' operator takes no
  account of record formats.
  Example:
           %record %format F(%integer X, Y, Z, A)
           %record %format Q(%byte %integer %array B(0:15))
           %record (F) J
           %record (Q) %array K(0:100)
            :
            :
           J = K(1)
           K(1) = 0; ! K(1)_B(0), K(1)_B(1), ..., K(1)_B(15)
                     ! all set to binary zeros.
  {IMP77: A %record %format %spec statement is provided to enable a
  record format to be referred to before it has been declared.  A
  statement of the form
           %record %format %spec <name>
  specifies a record format identifier.  Until the format is declared
  fully in a %record %format statement the identifier may only be used
  in the declaration of record reference variables (described below).
  Example:
           %record %format %spec Y
           %record %format X(%record (Y) %name P, %real VALUE)
           %record %format Y(%record (X) %name Q, %integer VALUE)
     Record formats of this sort are useful in list processing when the
     items in the list are records of alternating format X, then Y, then
     X, etc.}
Reference variables
  A reference variable (or "pointer variable") is one which has as its
  value, not a constant, but a reference to a variable of a specified
  type.  Reference variables are declared in the same way as the
  variables to which they can refer, but with the suffix %name added.
  Example:
           %integer A
           %integer %name AREF
  When a reference variable is declared, space is allocated for a
  reference to a variable of the corresponding type and precision.  The
  operator "==" is used to establish the reference.  Once a reference is
  established, all references to the reference variable will be
  redirected to the variable which it references.  Note that the
  reference can be established before the referenced variable has been
  assigned a value.  Reference variables are often used in conjunction
  with store mapping facilities (see Section 6).
  Example:
           %integer %name N
           %integer B
           N == B; ! Reference established
            :
            :
           N = 10; ! Assigns 10 to B
            :
  In exactly the same way, a reference to an array can be set up in an
  %array %name variable of the appropriate type.
  Example:
           %real %array %name P
           %real %array Q(0:27)
           %real %name Z
            P == Q; ! Reference established.
             :
             :
            P(25) = 10.3; ! This puts 10.3 into Q(25).
            Z == P(27)
             :
             :
            Z = 0; ! This sets the 27th element of Q to zero.
             :
  The examples above have been of reference variables of arithmetic
  types.  However, string and record reference variables may also be
  used.
  Example:
           %string(20)%name SREF
           %string(20) S
            :
           SREF == S
            :
  A maximum size must be specified for a string reference variable, as
  for a string variable.  A string reference variable can only refer to
  a string variable whose maximum size is equal to that of its own.
  However, where a string reference variable is required to refer to
  several strings of different maximum sizes, the form
           %string(*)%name <var>
  may be used.
  {EMAS IMP80: the string variable maximum size may be omitted.  Whether
  it is or not, the EMAS IMP80 compiler treats the size specification as
  '(*)'.}
  In the case of record reference variables, the format of the record to
  be referenced must be specified in the reference variable declaration.
  Example:
           %record %format RECFORMR(.....)
           %record (RECFORMR) REPORT
           %record (RECFORMR) %name REP2
  A reference variable of type %record %name is assigned to by using the
  '==' operator (as before), where the right-hand operand is a reference
  to a record location with the same format as that specified for the
  reference variable.
  Example:
           %record %format F(.....)
           %record (F) %name F1
           %record (F) Q, R
           %record (F) %array A(1:10)
           %record (F) %array %name Z, W
           F1 == Q;     ! Makes F1 a synonym for record Q
           F1 == A(10); ! Makes F1 a synonym for 10th element of A
           Z == A;      ! Makes Z a synonym for A
  {EMAS IMP80: the record location referenced by the right-hand operand
  does not have to have the same format as the record reference
  variable.}
  {IMP77: when a record reference variable is declared, the format can
  be specified as (*), meaning that the variable can refer to a record
  of any format.  The reference variable can have no associated
  sub-fields; it is only of use when it is passed as a parameter to a
  procedure, to be pointed at subsequently within the procedure by a
  reference variable with a specific format.}
  Note that records may contain sub-fields of type %record %name.  In
  this case the format of the record name sub-field may be the record
  format being described (c.f. sub-fields of type %record).
  The following example illustrates how the recursive nature of the
  format and sub-field format definitions facilitates the creation of a
  list structure:
  Example:
           %record %format F(%integer DATA, %record (F) %name LINK)
           %record (F) %array P(1:1000)
     The structure may be initialised as follows so that the 'link'
     field of each element of the record array P 'points' to the
     subsequent element:
           %record %format F(%integer DATA, %record (F) %name LINK)
           %record (F) %array P(1:1000)
           %record (F) END
           %integer J
           P(J)_LINK == P(J+1) %for J=1,1,999
           P(1000)_LINK == END
            :
            :
           %if P(J)_LINK == END %then ....
     Note how the link field of the last record in the chain is set to
     point to the record END.
%own, %constant and %external
  Additional properties can be given to variables by means of the
  prefixes %own, %constant (which can be abbreviated to %const) or
  %external added to the type in their declarations.
  An %own variable is allocated storage in such a way that it preserves
  its value between successive entries to the block or procedure in
  which it is declared.  It can be initialised in its declaration
  statement.  An %own variable can be used in any circumstances in which
  a normal variable of the corresponding type can be used.
  A %constant variable is declared in a similar manner to an %own
  variable, but it cannot be changed from its initial value.  Constant
  variables are also known as "named constants", which better describes
  them, in that they have all the attributes of constants.  Note that
  they do not have addresses (see ADDR, described in Section 6).
  Wherever a constant is permitted in an IMP80 program, a "constant
  expression" can be used instead.  A constant expression is one which
  can be evaluated at compile-time, i.e. its operands are constants or
  named constants.
  Example:
           %string (73) DELIVERY
     can be replaced by
           %constant %integer MAXNAME=20, MAXADDRESS=52
           %string (MAXNAME+1{for the newline}+MAXADDRESS) DELIVERY
  The constant integer NL is predefined: it contains the code value for
  the newline character.
  The constant long real PI is predefined.  It is the value of pi to the
  long real precision of the implementation; where 64 bits are used to
  hold a long real, this is 3.141592653589793.
  An %external variable is a special form of %own variable which is used
  to provide communication between sections of program compiled
  separately (see "External linkage", 3.3).  An %external variable can
  be used in any circumstances in which a normal variable of the
  corresponding type can be used.
  %own, %constant and %external arrays are normally one-dimensional, but
  need not be.
  Examples:
           %constant %byte %integer NUL=0, CR=13, DEL=127, FF=12
           %own %long %real  RMIN = -3.5@-4,
                             RMAX = 17.23614@10
  The initial values to be assigned to %own, %constant and %external
  variables are specified when the variables are declared.  A variable
  can be followed by =<cexpr> , where <cexpr> is a constant or constant
  expression of the appropriate type.  (A constant expression is one
  which can be evaluated at compile-time, i.e. made up of constants or
  variables of type %constant.)  The variable is initialised to the
  value of <cexpr>.
  If no initial value is specified, the value assigned by default is
  implementation-dependent.
  {EMAS IMP80: %own, %constant and %external variables which are not
  assigned initial values are set to binary zeros.}
  {IMP77: %own, %constant and %external variables which are not assigned
  initial values are undefined, and any attempt to use them before they
  are assigned will cause an event ("unassigned variable") to be
  signalled.}
  {IMP77: the '<-' assignment operator can be used in %own, %constant
  and %external variable initialisation.}
  Any identifier being declared as %external may be given an "alias".
  The details of this facility are described in Section 3.3.
  An %own, %constant or %external array is initialised by appending a
  list of values to its declaration.  Only one array may be declared per
  statement.  Each element of the array must have a corresponding value
  with which it is to be initialised.  In order to simplify this, each
  value may be followed by a repetition count in parentheses, and an
  asterisk, (*), may be used to represent the number of remaining
  elements of the array.  If the array is multi-dimensional, the order
  in which the array elements are assigned the initial values is
  implementation-dependent.
  {EMAS IMP80 and IMP77: in a two-dimensional array whose first element
  was (1,1), the order would be (1,1), (2,1), (3,1), i.e. first
  subscript changing fastest.}
  Examples:
           %external %integer %array VALUES(-3:7) = %c
                          17,  4, 23, -2,  3(4),  7,  1(2)
           %constant %integer RED=1, ORANGE=2, YELLOW=4, GREEN=8,
                            BLUE=16, INDIGO=32, VIOLET=64, WHITE=127
           %own %byte %integer %array COLOUR(1:22) = %c
                       RED, VIOLET(3), BLUE+GREEN, VIOLET,
                       INDIGO+ORANGE+BLUE, YELLOW(2), WHITE(*)
  The {...} form of comment is useful for commenting array
  initialisation.
  Example:
           %own %integer %array OPCODE(0:20) =  %c      {opcode values}
                    16_5800,    16_4800,    16_5000,    16_4000,
           {           L           LH          ST          STH
                    16_5A00,    16_5B00,    16_5C00,    16_5D00,
           {           A           S           M           D
                    16_1A00,    16_1B00,    16_1C00,    16_1D00,
           {           AR          SR          MR          DR
                    -1(*)                               {all the rest}
  %own, %constant and %external strings can be likewise initialised,
  with string constants or constant expressions at the time of their
  declaration.
  Example:
           %own %string(19) FILENAME = "ERCC00.TEST"
           %constant %string(6)%array F(0:4) = "Peter", "Mac", ""(3)
  Records may also be declared as %own, %constant or %external, but
  these may not be initialised at the time of declaration.
  {EMAS IMP80: the space allocated to such records is filled with binary
  zeros.}
  {IMP77: the values of the sub-fields of such records are undefined.}
  Variables of type %constant %record cannot be assigned at all, unless
  some implementation-dependent method is provided; see Appendix B.
  Reference variables may also be declared as %own, %external or
  %constant.  If initialisation is permitted in the implementation, then
  the initialisation establishes the initial reference in an
  implementation-dependent manner.  This facility is only useful for
  unusual system programming features.
  {Example:
     EMAS IMP80: the following declaration is valid:
           %constant %integer %name K INST PER SECOND = X'80C000C0'
     IMP77: the following declaration is valid:
           %constant %integer %name K INST PER SECOND == X'80C000C0'
     The value to which the integer name variable is being initialised
     specifies a particular storage location, and is therefore system
     dependent.}
  The initialisation of constant array name variables, if available, is
  described in Appendix B.
Constants
Constant values can be assigned to variables.  In general, the type of
the constant must be the same as the type of the variable, although an
automatic type conversion is carried out on a constant of integer type
before assignment to a variable of real type.
Decimal constants
  Decimal Constants are written in a straightforward notation:
        2.538    1     .25
  The exponent, where present, consists of the symbol @ followed by an
  optional sign and decimal digits:
        -17.28@-1   1@7
  The type of a decimal constant depends on its value.  It is of integer
  type if it has no fractional part, i.e. no decimal point in its
  specification and the exponent (if present) is non-negative; otherwise
  it is of real type.  The particular real or integer type depends upon
  the magnitude or precision of the constant.
Base constants
  A base constant may be constructed by using the prefix
  <decimal constant>_  to specify the base (up to a maximum of 36) of
  the subsequent constant.  The letters A, B, ..., Y, Z are used to
  represent the digits 10, 11, ..., 34, 35.
  Example:
           2_1010      ten in Binary
           8_12        ten in Octal
           9_11        ten in base nine
           16_A        ten in Hexadecimal
  An alternative form is provided for constants to bases 2, 8 and 16.
  The constant is written with the digits enclosed in single quotes and
  preceded by a code letter for the base, the codes being B for base 2
  (Binary), K for base 8 (Octal) and X for base 16 (Hexadecimal).
  Example:
           B'1010'     ten in Binary
           K'12'       ten in Octal
           X'A'        ten in Hexadecimal
  Either upper or lower case letters may be used in this form of
  constant, but spaces may not occur.
  When a program is to be used on other machines, care should be taken
  in the use of constants as the values of the constants may vary,
  particularly in a transfer from a machine using ones-complement
  arithmetic to one using twos-complement arithmetic, and vice-versa.
  Base constants are of type %integer.
  {IMP77: the base can have any positive integer value.  However, if it
  is greater than 36 then not all the digits will be representable by
  0...9 and A...Z.
  Example:
           I = 256_1234
  where I is a four-byte integer, assigns the values 1, 2, 3 and 4
  respectively to the four bytes of I.}
  {IMP77: a base constant can include a decimal part, in which case it
  is of type %real.
  Examples:
           3_0.1 {= 1/3}
           16_3.102A9 }
Character constants
  The ASCII code value of any character may be obtained as an integer
  value by enclosing the character in single quotes.  When the required
  character is a single quote, it must be represented by two consecutive
  single quotes.
  Examples:
           'A', 'a', '+', 'o', '"', '''', ' ', '
           '
  Note the last three examples which represent the code values for
  single quote, space and newline, respectively.  The predefined named
  constant NL may be used in place of the rather cumbersome form of a
  newline character enclosed in quotes.
  The code values for several characters may be packed together to form
  a single integer constant by enclosing the characters in single quotes
  and giving the prefix M.
  Examples:
           M'over', M'MAX', M'1+2', M'*@@#'
  The value of the constant is calculated by evaluating the expression:
           (..(c1<<b + c2)<<b + c3)<<b + ...
  where c1, c2... are the characters in the order specified, and b is an
  implementation-defined constant (commonly 8).  The number of
  characters which can be packed into an integer in this way is
  (no. of bits in an %integer)//b .
  Character constants are of type %integer.
String constants
  A %string constant is a sequence of characters enclosed in double
  quote characters, a double quote being represented inside a string
  constant by two consecutive double quotes.  The maximum number of
  characters allowed in the string is implementation dependent, but is
  not usually less than 255.
  Examples:
           "starting time"
           "x-y*4+2"
           "red"
           "HOOD"
  The null string, a string of no characters, is permitted and is
  represented by two consecutive double quote characters ("").
  A string constant of <n> characters is of type %string(<n>).
Named constants
  These are treated in this manual as variables of type %constant (see
  Section 2.2.5).
Arithmetic operators & expressions
Arithmetic operators
  There are two assignment operators for use with arithmetic
  expressions:
              =  equals
              <- jam transfer
  Where the = operator is used, the expression on the right-hand side is
  evaluated and the value obtained is assigned to the destination
  indicated by the left-hand side, provided that the lengths and types
  are compatible.  A fault occurs if an attempt is made to assign too
  large a value to a variable using this operator.
  Where the <- is used, only as many bits as will fit the location
  designated by the left hand side are assigned, starting with the least
  significant bits.
  In general the arithmetic assignment instruction assigns the result of
  evaluating an arithmetic expression to a variable.  Only the result of
  an integer expression may be assigned to an integer variable, but the
  result of an integer or real expression may be assigned to a real
  variable.
  The following operators may be applied to real and integer variables
  in arithmetic expressions:
        _______________________________________
       |                                       |
       |      +        addition                |
       |      -        subtraction             |
       |      *        multiplication          |
       |      /        real division           |
       |     //        integer division        |
       |      \        real exponentiation     |
       |               (e.g. Y\3 = Y cubed)    |
       |     \\        integer exponentiation  |
       |_______________________________________|
  The established order of precedence for the arithmetic operators is
  given in the following table, starting with the highest.  Operators on
  the same horizontal line of the table have equal precedence.
           _________________________
          |                         |
          |   \       \\            |
          |   *        /       //   |
          |   *        -            |
          |_________________________|
  Parentheses may be used to override the natural order of evaluation of
  an expression or to remove ambiguity.  Where operators are of equal
  precedence, left-hand precedence pertains as in normal mathematical
  usage.
  Examples:
           A-B+C    is equivalent to  (A-B)+C
           A-(B+C)                    (A)-(B+C)
           A/B*C                      (A/B)*C
           A/(B*C)                    (A)/(B*C)
           A**B*C                     (A**B)*C
           A**(B*C)                   (A)**(B*C)
  The one exception to the left-hand precedence rule is that consecutive
  exponentiations are evaluated from right to left; thus A**B**C is
  evaluated as A**(B**C), not as (A**B)**C.
Arithmetic expressions
  An arithmetic expression is a sequence of operators and integer or
  real operands obeying the elementary rules of algebra.  Expressions
  may be real or integer according to context.  Apart from the rules for
  operator precedence given above, no assumptions may be made about the
  order of evaluation of expressions.
Integer expressions
     An expression is evaluated as integer if it is being assigned to an
     integer variable, or passed as an integer value parameter, or
     occurs in a position where an integer expression is mandatory.
     All the operands and operators in an integer expression must yield
     integer values.  The operators available for use in integer
     expressions are:
        +  addition
        -  subtraction
        *  multiplication
       //  integer division.  This operator always yields an integer
           result.  The result consists of a quotient whose sign is
           determined algebraically and a remainder which is ignored.
           Note that dividend and divisor must both be integer
           expressions.
       \\  integer exponentiation.  This operator only operates on
           integer variables and always yields an integer result which
           is obtained by repeated multiplication.  The exponent must be
           an integer expression with a value in the range 0 < exp < 63.
     The precision used in evaluating integer expressions depends on the
     operands.  Variables of type %byte %integer, %short %integer and
     %half %integer are expanded to normal integer precision before the
     operation is carried out.  An operation between an integer variable
     and a long integer variable will be carried out by long integer
     arithmetic.  However, **** is anomalous in that long integer ****
     integer is carried out by repeated long integer multiplication but
     integer **** long integer is carried out by repeated integer
     multiplication.
     The following standard integer functions are provided (a 'standard'
     procedure is one which is predefined; see Section 7):
        %integer %function IMOD(%integer I)
           This function returns the modulus (absolute value) of the
           parameter.
           {IMP77: the modulus of an integer expression can also be
           obtained by enclosing the expression between vertical bars;
           e.g. |I-J| .}
        %integer %function INT PT(%long %real L)
           This function returns the integer part of L, any truncation
           being towards zero.  Hence INT PT(-1.5)=-1.
           An event is signalled if the result cannot be held in an
           integer variable.
        %integer %function INT(%long %real L)
           This function returns the nearest integer to the parameter,
           truncation being towards zero.  It is thus equivalent to:
                 %integer %function INT(%long %real L)
                    %result = INT PT(L+0.5)
                 %end
Real expressions
     All the operands and operators in a real expression must yield real
     or integer values, and assignment can only be made to a real
     variable.  Integer values will automatically be converted into
     their real equivalents before being used.
     The operators available for use in real expressions are:
        +  addition
        -  subtraction
        *  multiplication
        /  division
       **  real exponentiation.  This always yields a real result.
           Where the exponent is an integer expression, the operation is
           carried out by repeated multiplication.  A negative exponent,
           e.g. X**(-4), is evaluated as 1/X**4.
           {EMAS IMP80: where the exponent is a real expression, the
           result is obtained by using the standard functions LOG and
           EXP, and events relating to these functions may be
           signalled.}
     A real expression is evaluated to single precision until a long
     real variable is encountered.  Thereafter the expression is
     evaluated to double precision.  Double precision work is time and
     space consuming and should only be used when strictly necessary to
     preserve accuracy.  However, it is often required with
     floating-point arithmetic where loss of accuracy may occur in
     addition and subtraction due to cancellation of significant
     figures.
     The following standard real functions are provided (a 'standard'
     procedure is one which is predefined; see Section 7):
        %long %real %function FRAC PT(%long %real L)
           The fractional part of the parameter L is returned as the
           result.  Note that the fractional part is always treated as
           being greater than or equal to zero; e.g. FRAC PT(-4.6) is
           0.4 .
        %long %real %function MOD(%long %real I)
           This function returns the modulus (absolute value) of the
           parameter.
           {IMP77: the modulus of an real expression can also be
           obtained by enclosing the expression between vertical bars;
           e.g. |X-Y| .}
        %long %real %function FLOAT(%integer N)
           The floating-point equivalent of the integer parameter is
           calculated and returned as the result.
Logical ops and expressions
  Logical operations are performed on bit patterns stored in integer
  variables, which may be of any of the permitted lengths.  Before the
  operation is carried out, byte, short, and half integer variables are
  made up to full integer length in one of two ways, according to the
  length of the initial variable: a) byte and half integers are made up
  by filling the left hand bits with zeros; b) short integers are made
  up by sign extension, i.e. the leftmost bit of the variable - the sign
  bit - is propagated leftwards until the necessary number of bits have
  been obtained.  Where necessary, integers are made up to long integer
  precision by sign extension.
  There are two assignment operators available for logical expressions.
     =  equals treats the result of the logical operation as a signed
        integer and attempts to perform an arithmetic assignment to the
        designated variable.  Hence it is not always possible to put the
        result back into a variable of the same precision as that in
        which an operand of the logical expression was originally held.
    <-  jam transfer copies the bit pattern of the expression indicated
        by the right hand side into the variable indicated by the left
        hand side, starting with the least significant bits and stopping
        when the variable has been filled.
  The choice of assignment operator depends on the context of the
  program.
  The following is a list of the logical operators available, excluding
  the assignment operators discussed above.
           _________________________
          |                         |
          |   <<       left shift   |
          |                         |
          |   >>       right shift  |
          |                         |
          |   &        and          |
          |                         |
          |   !        or           |
          |                         |
          |   !!       exclusive or |
          |                         |
          |   \ (or ~) not          |
          |_________________________|
  The shift operators allow the programmer to move the bit pattern of an
  integer of any length to the left or right by a number of places less
  than the number of bits in the specified integer.
  Example:
           J = I>>N
     This causes integer I to be shifted to the right the number of
     places specified by N and the result stored in integer J.  If I or
     N are of less than integer precision they will be made up to
     integer precision, as described above, before the operation takes
     place.
  In a left shift, bit positions vacated at the right hand end are
  filled with zeros and bits shifted off the left hand end are lost.  In
  a right shift, bit positions vacated at the left hand end are filled
  with zeros and bits shifted off the right hand end are lost.  Note
  that a shift of more than N-1 characters will result in an integer
  whose value is implementation- dependent, not necessarily zero-filled.
  The operators &, !, !! are carried out on a bit-by-bit basis between
  the patterns stored in two integer variables.  Where one operand is a
  long integer, the other will be made up to long integer by sign
  extension.
  'and' (&)            produces a pattern containing a 1-bit where the
                       two source patterns both have 1-bits and
                       containing 0-bits elsewhere.
  'inclusive or' (!)   produces a pattern containing a 0-bit where the
                       two source patterns both have 0-bits and
                       containing 1-bits elsewhere.
  'exclusive or' (!!)  produces a pattern containing a 1-bit where the
                       bits in the source patterns are different and
                       contains 0-bits elsewhere.
  These rules are summarised in the following table:
              ______________________________
             |            |                 |
             |  Operands  |  &     !    !!  |
             |____________|_________________|
             |            |                 |
             |   0  0     |  0     0     0  |
             |            |                 |
             |   0  1     |  0     1     1  |
             |            |                 |
             |   1  0     |  0     1     1  |
             |            |                 |
             |   1  1     |  1     1     0  |
             |____________|_________________|
  The \ operator operates on a single operand to invert the value of
  each bit; that is, 0-bits become 1-bits and vice versa.
  Example:
     If X contains the bit pattern    01....0100110011
     then \X is                       10....1011001100
     thus \X + X is                   11....1111111111
  Arithmetic and logical operators may occur in the same arithmetic
  expression.  The established order of precedence, starting with the
  highest, is:
               ________________________
              |                        |
              |  \                     |
              |                        |
              |  **    >>    <<        |
              |                        |
              |  *     /     //    &   |
              |                        |
              |  +     -     !     !!  |
              |________________________|
  Operators given on the same line in the above table have equal
  precedence.
  Example:
           %integer I, J
           ! Variables of type %integer are assumed to have 32 bits
           ! in this example.
           %byte %integer %array B(0:3)
            :
           I = ....
            :
           B(J//8) = I>>(24-J) & X'FF' %for J = 0,8,24
            :
     In this example, a 32-bit integer I is copied, in groups of 8 bits,
     into the byte integer array B.
  Note that X'FF' represents a bit pattern of eight 1s in the least
  significant end of the specified location and zeros elsewhere.
String ops and expressions
  There are three assignment operators available for use with strings:
  "=", "<-" and "->".
  Where "=" is used, the string expression on the right-hand side is
  evaluated and assigned to the string location specified on the
  left-hand side.  The string expression must evaluate to a string
  constant which is no larger than the maximum which the left-hand
  string location can hold.  A run-time error will occur if the
  left-hand string location overflows.
  "<-" is known as the "jam transfer" operator.  It will assign to the
  location specified on the left-hand side only as many characters of
  the right-hand string as will fit.  Any remaining characters from the
  right-hand end of the string being assigned will simply be omitted and
  no error will occur.
  "->" is known as the "string resolution" operator.  It is used
  exclusively for the manipulation of strings.  Its effect is described
  below.
  Another operation exclusive to strings is "concatenation".  This
  allows them to be joined in a prescribed order.  The strings to be
  concatenated are listed in the required order and separated by the
  symbol '.' .
  Example:
           %begin
              %string(65) S
              %string(15) NAME1, NAME2, NAME3
              %string(20) ADDRESS
              NAME1 = "Peter "
              NAME2 = "John "
              NAME3 = "Smith
           "
              ADDRESS = "12 Bothwell Drive"
              S = NAME1.NAME2.NAME3.ADDRESS
              PRINTSTRING(S)
              NEWLINE
           %end %of %program
     String S is assigned the concatenated characters of the four
     strings NAME1, NAME2, NAME3 and ADDRESS.  Thus the printed output
     would read
              Peter John Smith
              12 Bothwell Drive
     Note that the string NAME3 has a newline character in the text
     after the name Smith.
  The only type of string expression in IMP80 is that produced by
  concatenation, for which bracketed expressions are neither required
  nor permitted.
  String resolution allows a string to be searched for a specified
  substring of characters.  If this substring does not occur in the
  string being resolved, an error condition occurs.  If, however, the
  substring is found on searching the string from left to right, then
  all characters to the left and right of the substring will be stored
  respectively in two strings specified to the left and right of the
  substring in the resolution instruction.  The substring, which may be
  a string expression (a constant, a variable or a concatenation), is
  enclosed in parentheses with a string identifier on either side, each
  of these three elements being separated by '.' .
  Example:
           S = "ERCC00.FLAG"
           S -> A.(".").B
           ! A now contains "ERCC00" and B contains "FLAG".
     Thus string S has been split into two smaller strings neither of
     which include '.' .  The same exercise would be accomplished by the
     following:
           S = "ERCC00.FLAG"
           X = "."
           S -> A.(X).B
  The substring may be a concatenation:
  Example:
           FIRST NAME = "John"
           SURNAME = "Smith"
           NAME AND ADDRESS = "Peter John Smith, 12 Bothwell Drive"
           NAME AND ADDRESS -> A.(FIRST NAME." ".SURNAME).B
  {EMAS IMP80: multiple resolution is permitted; it is treated as a
  series of simple resolutions.
  Example:
           S = "WINSTON SPENCER CHURCHILL"
           S -> A.(" ").B.(" ").C
     The above resolution will be treated as
           S -> A.(" ").PRIV %and PRIV -> B.(" ").C}
  The strings used to store the characters which occur before and after
  the specified substring may be omitted, in which case the characters
  are discarded.
  Examples:
           S = "ERCC00.FLAG"
           S -> (".").B
           ! B now contains "FLAG".
           S = "ERCC00.FLAG"
           S -> A.(".")
           ! A now contains "ERCC00".
           S = "ERCC00.FLAG"
           S -> (".")
           ! Resolution would fail if "." did not occur in S.
           ! There are no other products of this resolution.
  Since a resolution must either succeed or fail, it may be used as a
  simple condition.
  Examples:
           S = A.B %while S -> A.(" ").B
           ! This statement removes all spaces from string S.
           %if S -> A.("/").B %then T = A." or ".B
  Note that, although the string resolution is used here as a condition,
  it is nonetheless carried out if it can be (i.e. if the condition is
  true).
  There are four procedures provided for the manipulation of strings.
  %byte %integer %map CHAR NO(%string(*)%name S, %integer N)
     This map returns a reference to the Nth character of string S.  An
     event is signalled if N is negative or greater than the current
     length of S.
  %byte %integer %map LENGTH(%string(*)%name S)
     The result is a reference to a variable containing the current
     length of the string S.
  %string(*)%function SUBSTRING(%string(*)%name S, %integer I, J)
     The result is the substring of S comprising the Ith to Jth
     (inclusive) characters of S.  An event is signalled unless
     1 <= I <= LENGTH(S) and I-1 <= J <= LENGTH(S).  If J=I-1 then a
     null string is returned.
  %string(1)%function TO STRING(%integer I)
     The result is a string of length 1 comprising the character defined
     by the least significant byte of integer I.
     Example:
              %string(80) S
              %integer %name L
               :
               :
              L == LENGTH(S)
              L = L-1 %while L>0 %and CHARNO(S,L) = ' '
              ! This example shows how to delete trailing spaces
              ! from a string.
Record operators
  The only record operators taking complete records, rather than record
  sub-fields, as their operands are the assignment operators '=', '<-'
  and '=='.  These are described above, in 2.2.3/9 .
Blocks and Procedures
Block structure and storage
IMP is a block-structured language.  A block is a sequence of statements
of which the first is %begin and the last is %end.  The program itself
is regarded as a block, and the first %begin encountered is interpreted
as the start of the program.  The statement %end %of %program, rather
than %end, is used to indicate that the end of the program has been
reached.  Blocks may be nested to a depth determined by the particular
implementation.
Within each block, variables and constants to be used must be declared
at the head of the block (and before any %on %event statement), unless
they have already been declared at the head of an outer block.  Labels
and switch labels are always local to a block; thus it is impossible to
jump from one block to another.  Keywords which occur in pairs such as
%cycle ... %repeat must have both elements within the same block.
Example:
        %begin
           %integer I, J
           %real %array A(1:10)
            :
           %real X, Y
            :
            :
           %begin
              %real Z, P
              I = 4
              J = 10
               :
               :
           %end
            :
            :
        %end %of %program
  I and J have been declared at the head of the outermost block and may
  thus be referred to from any inner blocks of the program.
If, however, a variable is declared which has the same identifier as a
variable already declared in an outer block of the program, then use of
that identifier will refer to the variable most recently declared.
Example:
        %begin
           %integer I, J, K
           %real X, Y
           I = I+1
            :
            :
           %begin
              %real I
              I = 4.106
               :
               :
              %begin
                 X = I
                  :
                  :
              %end
           %end
        %end %of %program
  I is first declared as an integer type and will be allocated storage
  accordingly.  The first use of I refers to this integer location.
  Within the next block, however, I is redeclared as a real variable.
  Now space is allocated for a real variable and within this block (and
  any deeper blocks of the program - unless I is again redeclared) use
  of I refers to the real location and the integer location of the outer
  block remains untouched.
When a variable is accessible it is said to be in scope.  A variable is
global to a block within the block in which it is declared, if it is
accessible there.  In the example above, X, Y, J and K are global to
both inner blocks.
The redeclaration of variables is permissible because of the way in
which storage space is allocated to a program.
Each program can be considered to have space allocated to it on what is
called the stack.  The stack is an area of store and the stack space
given to each program has a prescribed layout, as shown in the following
diagram.
                                      stack pointer-----------\
                                                              |
                                                              |
 _____________________________________________________________v________
| Program  |  Constants,          |%own variables, | Cells in |  Free  \
| Code     |  %constant variables,|%own arrays     | use      |  cells
|          |  %constant arrays    |                |          |      ...
|__________|______________________|________________|__________|________
\________________________________/ \___________________________________/
                |                                   |
              read only                           read and write
The "read only" area contains the object code of the program as produced
by the compiler, and also any of the constant variables declared by the
program.  This area cannot be altered during execution of the program.
The "read and write" area has own variables stored first and thereafter
space is allocated according to the requirements of the program.  Each
cell will be as big as is required by the entity stored in it: thus an
integer variable occupies only a small cell, whilst a complex record
variable may require a very large cell.
The stack pointer holds the address of the next free location in store.
Information stored on the stack is kept as compact as possible though
with due regard for alignment considerations.  This is of particular
significance on paging machines, as it allows the program to run with a
minimum number of page faults.
The following example illustrates the stack mechanism.
Example:
        %begin
           %real A,B,C
           %integer I,MAX
           %real %array X(1:3), Y(1:4)
  As a result of the above declaration, the stack might look like this:
  ST1                                                        ST2---\
  |                                                                |
  v________________________________________________________________v_
  |  |  |  |  |    |   |    |     |     |   |    |     |     |     |
  | A| B| C| I| MAX|###|X(1)| X(2)| X(3)|###|Y(1)| Y(2)| Y(3)| Y(4)| ..
  |__|__|__|__|____|___|____|_____|_____|___|____|_____|_____|_____|___
  ST1 is the position of the stack pointer before the %begin, and ST2 is
  its position after the declarations.  The shaded areas indicate
  portions of store which contain information essential to the program
  (such as array dimensions) but which cannot be accessed by the
  program.
  The stack pointer may be advanced by any further declarations or by
  activity initiated by the instructions of the program.
  On entry to a new block or procedure, the stack pointer is advanced as
  necessary to cope with new declarations, etc.  On exit from the block
  (or procedure) it returns to its last position prior to entry to that
  block (or procedure).
  Storage space for fixed variables such as %real or %integer types is
  determined at compile time, but arrays with dynamic bounds cannot be
  allocated space until the values of the bounds are determined at run
  time.
  Since the block structure of a program is so closely related to the
  allocation of store, skilful use of blocks can lead to economical use
  of store.  Consider the following examples.
Example:
        %begin
           %integer N
           %cycle
              READ(N)
              %exit %if N=0
              %begin
                 %integer I
                 %integer %array A(1:N)
                 READ(A(I)) %for I=1,1,N
                  :
                  :
              %end
           %repeat
        %end %of %program
  The required size of the array is read in the outer block and the
  array itself declared in the inner block.  Thus the space used by any
  one set of data will be recovered when the inner block is left, so
  allowing one to repeat the process without incurring successively
  increasing demands for storage space.
Example:
        %begin
            :
            :
           %begin
              %real %array XYZ(1:5000)
               :
               :
           %end
            :
            :
           %begin
              %integer %array IJK(1:20, 1:250)
               :
               :
           %end
            :
            :
        %end
Since the declarations at the head of a block are cancelled on executing
the %end of the block, it is often possible to economise on storage
space if a program consists of several distinct tasks, each requiring
large amounts of space.  The above example illustrates the point.
Procedures can be used in a similar way to economise on store.
Events
  During the execution of a program, events may occur which normally
  cause the program to terminate with an error message.  However, there
  is a mechanism which allows events to be intercepted and used to
  control the subsequent execution of the program.  This mechanism is
  activated by the use of the %on %event statement.
  The %on %event statement (which may occur only once in any block) is
  used to introduce a block of statements which is only executed if one
  of the specified events occurs.  The form of the %on %event block is:
           %on %event <nlist> %start
              <executable instructions>
           %finish
  <nlist> is a list of integers in the range 1 to 14 inclusive, where
  each number refers to a specified class of error as follows:
           1  Overflow
           2  Excess resource
           3  Data error
           4  Invalid data
           5  Invalid arguments
           6  Out of range
           7  Resolution failure
           8  Undefined value
           9  I/O error
          10  Library procedure error (e.g. SQRT negative)
       11-14  Available for user definition
  {EMAS IMP80: event 11 is used by the ERCC Graphics Package.}
  {IMP77: there are two extensions to the range of event numbers:
           Event 0 is defined - Termination
           Event 15 is available for user definition}
  {IMP77: the form
           %on %event * %start
  is permitted.  It is a shorthand way of specifying every event number;
  i.e. 0,1,...,14,15.}
  Up to 255 sub-events may be defined for each event, but these cannot
  be specifically intercepted and are necessarily
  implementation-dependent.  For example, not all machines distinguish
  integer overflow from real overflow.  Sub-events defined for some
  implementations are listed in Appendix B.
  The %on %event block must follow the declarations at the head of a
  block and may be regarded as the last declaration of the block.  The
  code within the %start ... %finish is not executed by entry through
  the head of the block, but is jumped to on the occurrence, during the
  execution of the block, of an event referenced by the event list.
  Following such a jump, the flow of control is determined by the
  contents of the %on %event block; the program does not resume at the
  point of the failure.
  An event may be forced to occur by the instruction
           %signal %event <const>, <exprn>
  where <const> specifies the event required and <exprn> is an optional
  integer expression (evaluating to an integer within the range 0-255)
  which may be used to specify sub-event information.
  The use of the %signal %event statement is the only way of causing a
  user-defined event (i.e. one which is not pre-defined by the
  implementation) to occur, although it can also be used with the
  predefined events (1-10).
  If an event is forced by a %signal %event statement in an %on %event
  block which includes the occurring event in its event list, a branch
  is not made to the head of that block, since such a branch would
  probably cause looping.  Instead, the event is traced up the stack
  through each superior block until either an %on %event statement
  including the occurring event in its list is found, or the user
  environment is left.  If a suitable %on %event statement is found,
  control is transferred to its %start ... %finish block.
  In parallel with these language statements, two standard integer
  functions are provided which enable the programmer to determine
  further information when an event occurs.  They may only be
  meaningfully called in a block which has an %on %event statement
  within it.
     %integer %function EVENT INF
        This function returns
                 (event no<<8) ! sub-event no
        for the last event which has occurred.  An error occurs at
        compile time if the function is called in a block with no %on
        %event statement, and an undefined value will result at run time
        if no event has in fact occurred when the function is called.
     %integer %function EVENT LINE
        This function returns the program line number at which the last
        event occurred during execution of the block in question
        (provided the program was compiled with line number updating;
        otherwise 0 will be returned).  If no event has occurred, an
        undefined value will result.
        If an event is not intercepted in the block in which it occurs,
        then it is traced up the stack through each superior block until
        either a suitable %on %event statement is encountered or the
        user environment is left, the diagnostic package being entered
        in the latter case.  When a suitable %on %event statement is
        encountered in an outer block, program control is transferred to
        its %start ... %finish block.
  As a result of these facilities it follows that, for example "input
  ended" may be detected and dealt with from within an external routine
  or a routine within a main program.
  Examples:
           %on %event 10 %start
              %if EVENT INF&255#8 %start
                 %signal %event 10, EVENT INF&255
                 ! All subevents except 8 are passed to outer block
              %finish
               :
               : ! Code to deal with subevent 8 of event 10
               :
           %finish
     System defined events
           %integer SUBCLASS, EVENTNO
           %constant %string(21)%array MESSAGE(1:2) = %c
                                               "Capacity exceeded",
                                               "Array bounds exceeded"
           %on %event 6 %start
              SUBCLASS = EVENT INF&X'FF'
              EVENTNO = EVENT INF>>8 & 15
              %if 1<SUBCLASS<=2 %then PRINTSTRING(MESSAGE(SUBCLASS)) %c
                %else PRINTSTRING("Invalid subclass")
              NEWLINE
              ->ERROR EXIT
           %finish
            :
            :
           ERROR EXIT: ....
     User defined events
           %integer SUBEVENT
           %on %event 12 %start
              PRINTSTRING("Event 12 has been intercepted")
              NEWLINE
              ->EVENT 12
           %finish
            :
            :
           SUBEVENT = 2
            :
            :
           %signal %event 12, SUBEVENT
            :
            :
           EVENT 12: .....
  {IMP77: a standard record map is provided to enable the programmer to
  determine further information when an event occurs.
     %record %format EVENT FM(%integer EVENT, SUB, EXTRA)
     %record(EVENT FM)%map EVENT
        EVENT returns a reference to a system-provided record which
        contains the parameters of the last event to have been
        signalled.  If no event has been signalled all the fields of the
        record are set to zero.
        Consult Appendix B and documents relating to specific
        implementations of IMP77 for details of the information
        returned.}
  Note that the event mechanism is designed for intercepting the
  occasional event only - overheads are high if the %on %event statement
  is used frequently.
Procedures
A procedure takes the form of a block in which the first %begin is
replaced by the procedure heading.  However, a procedure can only be
entered by execution of a procedure call statement, whereas a block is
entered when the %begin statement at the start of the block is executed.
There are three forms of procedure - %routine, %function (or %fn) and
%map.
Example:
        %begin
           %integer %array A(1:40)
            :
            :
           %routine CLEARA
              %integer I
              A(I) = 0 %for I=1,1,40
           %end {Of %routine CLEARA.}
            :
            :
           CLEARA
            :
            :
        %end %of %program
In this example, a procedure - routine CLEARA - is described and then
used by means of the procedure call statement 'CLEARA'.
CLEARA is effectively a named block: if the procedure heading were
replaced by %begin and the procedure description moved down to take the
place of the procedure call statement, the effect would be exactly the
same.  However, by making the block a routine - and thus giving it a
name - it is possible to call it at different places in the program
without repeating the description each time.
In addition, procedures can have parameters, passed to them via a
parameter list enclosed in brackets in the procedure call statement.
Example:
        %begin
           %integer F, T
           %string(31) %array A(0:99)
           %routine STRINGSORT(%string(*)%array %name X,
                               %integer FROM, TO)
              %integer L, U
              %string(255) D
               :
               :
           %end {Of %routine STRINGSORT.}
            :
            :
           STRINGSORT(A, F, T)
            :
            :
        %end %of %program
  In the above example, the procedure heading specifies the form of
  procedure (%routine) being described, gives the procedure the
  identifier STRINGSORT and describes the number, order and types of
  variables passed as parameters, using dummy names.  The procedure is
  entered when the call statement STRINGSORT(A, F, T) is executed.
The first line of a procedure has one of the following forms:
        %routine <name> (<parameter list>)
        <type> %function <name> (<parameter list>)
        <type> %map <name> (<parameter list>)
where <type> is one of the arithmetic types, or %string(<n>) or
%record(<format>), and (<parameter list>) is optional.
{EMAS IMP80: record functions and record maps are not available.}
A procedure must be declared before it is called.  If the procedure
itself is placed at the head of a block (as above) no further
declaration is needed.  Otherwise a specification (%spec) statement must
be placed amongst the other declarations at the head of a block, with
the procedure included later at the same textual level.  The %spec
statement is exactly like the procedure heading with %spec inserted
after the keyword %routine, %function or %map.
Example:
        %routine %spec STRINGSORT(%string(*)%array %name X,
                                      %integer FROM, TO)
It is most important to give the specification accurately: in
particular, the number and types of the parameters must be correct.
However, the names of the parameters in a specification statement are
not significant.
Variables used in procedures may be declared locally (as is the case for
L, U and D in the above example), but any information stored in them
becomes inaccessible on exit from the routine.  If the information
calculated by the procedure is to be preserved for use on subsequent
entries, it must be stored in global variables or %own variables
declared locally.
Global variables should be used with care in procedures.  Note that such
variables must be declared globally to the procedure itself: it is not
sufficient that they be declared globally to the procedure call.
Example:
        %begin
           %integer X, Y
           %routine CONVERT
              X = ....
              Y = ....
           %end
           CONVERT
            :
            :
           %begin
              %integer X
               :
               :
              CONVERT
               :
           %end
            :
            :
        %end %of %program
  The routine CONVERT, which has no parameters, operates on variables X
  and Y.  The first call of the routine uses X and Y as declared at the
  head of the program.  The second call occurs from within an inner
  block in which X has been redefined.  However, the procedure again
  uses the X declared at the head of the program, ignoring the redefined
  X of the inner block.
Information calculated by a procedure and stored in global variables is,
of course, accessible on exit from the procedure.
There are three categories of procedure which may be called by a
program:
  1) standard procedures, which are automatically available to all
     programs (see Section 7); for example, READ, INT PT;
  2) procedures described within the program;
  3) external procedures, which are compiled separately from the program
     (see Section 3.3).
Procedures can be nested: that is, a procedure can be defined inside
another procedure.  The scope rules apply as before.
Procedures can be used recursively: that is, a procedure can be called
from within itself.  The example in 3.2.1 of a sorting program
demonstrated the recursive use of routine STRINGSORT.  Obviously, some
criterion within the body of the procedure must eventually prevent the
procedure calling itself endlessly.  In the example, the recursive calls
of STRINGSORT are embedded in conditional instructions, thus providing
the necessary opportunity to stop the recursion process.
Parameters
  When a procedure with parameters is described, the heading statement
  contains dummy names for the variables passed as parameters.  These
  names are used in the description of the procedure and are referred to
  as "formal" names.  When the procedure is executed, however, "actual"
  parameters take the place of the formal parameters, and are specified
  in the parameter list of the particular call.  The parameters given in
  the procedure call must correspond in number, order and type with
  those specified by the procedure heading.  Local storage is allocated
  within the procedure for each formal parameter.
  Parameters fall roughly into two categories - those passed "by value"
  and those passed "by name".  When a procedure call is executed,
  parameters passed by value have their values assigned to the
  corresponding local variables.  An arithmetic expression may thus be
  passed as a parameter: it will be evaluated before entry to the
  procedure and its value assigned to the appropriate formal parameter.
  On exit, it will become inaccessible and any information stored in it
  will be lost.
  Example:
           %begin
              %real Z, Y
              %real %function AUX(%real X)
                  :
                  :
              %end
               :
               :
              Y = AUX(Z)
               :
               :
              Y = AUX(4.5*Z)
               :
               :
           %end %of %program
     Function AUX has formal parameter X of type %real.  Execution of
     the first call of the function will assign to X the value to be
     found in Z.  On executing the second call, the expression 4.5*Z
     will be evaluated and the result assigned to formal parameter X.
  Parameters passed "by name", however, are treated differently.  The
  local storage allocated for a parameter passed by name is a reference
  variable of the appropriate type.  In this case, when a procedure call
  is executed, the effect is that the local variable is "pointed at" the
  actual parameter, which must therefore be the name of a variable, and
  not an expression.  Thus, every reference in the procedure to the
  formal parameter is treated as if it were a reference to the actual
  parameter.  Parameters passed by name can be used to preserve
  information calculated by the procedure, for use on exit.
  Example:
     Given a procedure with heading
           %routine ALPHA(%integer BETA, %real %name GAMMA)
     the call
           ALPHA(J*K+4, R(M))
     has the effect of causing the following assignments to be carried
     out on entry to the procedure:
           BETA = J*K + 4
           GAMMA == R(M)
     (Note however that these two assignments as given may violate the
     normal rules for scope of names, because they use variables in
     scope at the point of the call to assign to variables local to the
     routine body.)
  Example:
           %begin
              %constant %string (1) SNL = "
           "
              %string(31)%array NAMES(1:99)
              %routine %spec STRINGSORT(%string(*)%array %name X,
                                        %integer F, T)
              %integer I, N
              READ(N) %until 1 <= N <= 99; ! Reject if not in range.
              READSTRING(NAMES(I)) %for I=1,1,N
              STRINGSORT(NAMES,1,N)
              PRINTSTRING(NAMES(I).SNL) %for I=1,1,N
              %routine STRINGSORT(%string(*)%array %name X,
                                  %integer FROM, TO)
                 %integer L, U
                 %string(255) D
                 %return %if FROM >= TO
                 L = FROM; U = TO
                 D = X(U)
                 %cycle
                    L = L+1 %while L < U %and X(L) <= D
                    %exit %if L = U
                    X(U) = X(L)
                    U = U-1 %while U > L %and X(U) >= D
                    %exit %if U = L
                    X(L) = X(U)
                 %repeat
                 ! Now L = U.
                 X(U) = D
                 L = L-1; U = U+1
                 STRINGSORT(X,FROM,L) %if FROM < L
                 STRINGSORT(X,U,TO) %if U < TO
              %end
           %end %of %program
     This program sorts a set of strings held in array NAMES.  Note the
     %routine %spec statement at the head of the block, with the routine
     description occurring later at the same textual level.
     There are three parameters passed to the %routine STRINGSORT; the
     two integers are passed by value, and the string array is passed by
     name.  Arrays can only be passed by name, to prevent unnecessary
     allocation of storage space and time-consuming copying.  When the
     routine is called the first time the parameters are treated as
     follows.  The string array NAMES is passed by name and thus all
     references to the formal parameter X within the body of the routine
     become references to NAMES; the actual value 1 will be assigned to
     formal parameter F; and the value stored in N will be assigned to
     formal parameter T.  On exit from the routine, NAMES will have its
     elements sorted, but the value of N will be unchanged.
  Strings may be passed as parameters to procedures.  Where a string
  name parameter is used, the length specified in the procedure heading
  can be replaced by * so that strings of any length (up to the allowed
  maximum) can be passed to that procedure.  In this situation run-time
  overflow checking is applied to the actual string passed to the
  procedure.
  In IMP, parameters called by name are assigned at the time of call.
  Thus if a routine with parameter list (%realname X, %integername I,
  ...) were called with parameters (A(J), J, ...) where A is the name of
  a previously declared real array, then on execution of the procedure
  every reference to X will refer to the element of A determined by the
  value of J on entry, no matter how J varies during execution of the
  procedure.
  Procedures, too, may be passed as parameters:
  Example:
           %begin
              %routine ONE(%routine PARAM(%integer X) )
                  :
                  :
              %end
              %routine TWO(%integer P)
                  :
                  :
              %end
              %routine THREE(%real X)
                  :
                  :
              %end
              ONE(TWO)
           %end
     Routine ONE has a single parameter, a routine with a single
     parameter of type %integer.
     Note that routine THREE cannot be passed as parameter to routine
     ONE because THREE has a parameter of type %real.  That is, the
     parameter list of the actual procedure passed must correspond with
     the parameter list of the corresponding formal procedure parameter.
To summarise: a formal parameter can be any of the following types:
  1) any arithmetic type (e.g. %long %real, %short %integer)
  2) %string(n)
  3) %record(format)
  4) any of the above followed by %array %name
  5) any type of procedure (i.e %routine, %function of any type, %map of
     any type)
Items (1) - (3) correspond to call by value; items (4) and (5) to call
by name.  The actual parameter in a call by value must be an expression
of the appropriate type; in a call by name it must be a "reference" to
an entity of the appropriate type.
Routines
  A routine call may be used exactly like an instruction.  When the call
  is executed, control is transferred to the routine, which executes
  until either the %end statement is reached or a %return statement is
  encountered.  Flow of control is resumed at the statement after the
  routine call.
  Example:
            :
            :
           %integer X,Y
           %routine CONVERT
              %if X < Y %start
                 X = X+Y
              %finish %else %start
                 X = X-Y
              %finish
           %end
            :
            :
           CONVERT
            :
            :
           CONVERT %unless X = 0
            :
            :
     Note that CONVERT uses global variables X and Y and that the result
     is stored in X on exit from the routine.  Note also the use of
     CONVERT in a conditional statement.
Functions
  A function calculates a value of the specified type (integer, real,
  string or record), and may be used in an expression exactly like an
  operand of that type.  The function terminates when an instruction of
  the form
           %result = <expression>
  is executed, and the value of the expression, which must be of the
  same type as that of the function, is returned to the statement making
  the call.
  {EMAS IMP80: record functions are not available.}
  Example:
           %integer %function SUMSQ(%integer A, B)
              %result = A**2 + B**2
           %end
           %integer X, Y, Z
            :
            :
           Z = SUMSQ(X,Y) - 3
            :
  The use of global variables in functions may have unusual side
  effects.  Such side effects can be difficult to detect and should be
  avoided.
  Example:
           %begin
              %integer I, J, K
              %integer %function SIDE
                 I = I+1
                 %result = J
              %end
               :
              K = I+SIDE
               :
           %end
     In the statement K = I+SIDE, there is no defined order of
     evaluation of operands in the expression on the right hand side of
     the assignment, but the value of I used in the expression depends
     upon the order of evaluation.  Thus the value of the expression is
     indeterminate.  The actual value computed depends on the
     implementation of IMP80 used.
  Functions may be of %string type; in this case the maximum length of
  the string which may be returned by the function is included in the
  specification and heading of the function.
  Example:
           %string(20)%function FIELD(%integer I)
  Functions may be of %record type; in this case the format of the
  record returned must be included in the specification and heading of
  the function.
  Example:
           %record %format RFA(%integer ONE, TWO, THREE)
           %record (RFA) %function TRIPLES(%integer ITEM)
            :
  {EMAS IMP80: record functions are not available.}
Maps
  A map (or "mapping function") calculates a reference to a variable of
  the specified type (integer, real, string or record) and may be used
  exactly like a variable of that type.  The map terminates when an
  instruction of the form
           %result == <ref to a variable of the same type as the map>
  is executed and the address of the given variable is returned to the
  calling instruction.  The "reference to a variable" on the right hand
  side of the %result statement can be a normal variable, a reference
  variable or a mapping function call.
  {EMAS IMP80: programmer-written record maps are not available.}
  {EMAS IMP80: a %result statement in a map can be of the form
           %result = integer expression
  where the integer expression gives the address of some location.  It
  usually includes a call of the function ADDR, described in Section 6.}
  {IMP77: the keyword %map may be replaced by %name %function or %name
  %fn.}
  Example:
           %integer X, Y, K
           %integer %map MIN
              %if X < Y %then %result == X %else %result == Y
           %end
           MIN = 0
           ! This statement is exactly equivalent to
           !       %if X < Y %then X = 0 %else Y = 0
            :
           K = MIN
            :
     Note the use of a map on the left-hand side or right-hand side of
     an assignment statement.
  Where a map is of %string type, the specification and heading must
  include a length - the precise maximum length of any string to which
  the map may refer.
  Example:
           %string(3)%map XA(%integer I)
  If a string map might refer to strings of varying length, then the
  procedure heading and declaration may have the symbol '*' in place of
  a specific length.  The map may then reference strings of any length
  up to the allowed maximum.
  Example:
           %string(*)%map XA(%integer I)
            :
  Where a map is of %record type, the specification and heading must
  include a record format.  The right hand side of a %result statement
  within a record map must refer to a record of the same type as the
  map, or to the standard record map RECORD (described in Section 6).
  Example:
           %record %format RF(%integer X, %string(10) TITLE)
           %record (RF) %map RM(%integer I, J)
              %record (RF) %name CURRENT
               :
              CURRENT == ...
               :
               :
              %result == CURRENT %if ...
               :
           %end {Of %record %map RM
           %string(15) HEAD
           %integer P, Q
            :
           HEAD = RM(P,Q+1)_TITLE
            :
           RM(17,3)_X = 24.6
            :
  {EMAS IMP80: programmer-written record maps are not available.
  However, a standard record map RECORD is provided; it is described in
  Section 6.}
  {IMP77: the record format given in the specification or heading can be
  replaced by (*), meaning that the reference returned by the map may be
  to a record of any format; the actual record format used depends on
  the context.
  Example:
           %record(*)%map SURVEY(%integer I, %real X)
  The standard map RECORD (described in Section 6) is of this sort.}
  A number of standard maps are provided.  These are described in
  Section 6.
External linkage
A complete program may be divided into separately compiled modules which
are linked before (or possibly while) the program is executed.  This
section describes the language facilities provided for setting up or
accessing a separately compiled module.
A procedure compiled separately from a program which uses it is called
an external procedure.  If a program uses an external procedure it must
contain an external specification statement.  This is of the same form
as the %spec statement described in Section 3.2, but with the prefix
%external.
Example:
        %external %string(*) %function %spec I TO S(%integer A)
An %external...%spec statement has the same effect as a normal %spec,
except that there is no description of the procedure later in the
program.  %external ... %spec statements may be given wherever other
%spec statements would be valid.
The keywords %system and %dynamic may be used in place of %external;
refer to the relevant system documentation for details of the effects of
these keywords.
External variables are also available.  If a program uses an external
variable it must include an external specification.  This is of the same
form as the variable declaration statement but with the keywords
%external and %spec added.
Examples:
        %external %integer %spec WAIT, CHOICE
        %external %real %array %spec MEAN(-6:6)
External files
  A file of external procedures and variables may be compiled.  Such a
  file differs from the structure of a program file (described in
  Section 1.2) in several respects:
   * There is no initial %begin.
   * %end %of %program is replaced by %end %of %file.
   * Variables declared outside any procedures must be %own, %constant
     or %external (described below).
   * The first statement of any procedure description can be preceded by
     the keyword %external; such a procedure can then be made accessible
     to other programs, as explained above.  If a procedure in the file
     is not %external then it is accessible only within the file, in
     accordance with the normal scope rules.
   * %begin/%end blocks are not allowed, except within procedures.
     {IMP77: a single %begin/%end block at the outer level is allowed,
     so long as it immediately precedes the %end %of %file statement.
     The block's %end statement and the %end %of %file statement can
     then be replaced by the single statement %end %of %program.}
   * Where an %external ... %spec statement in the file specifies an
     external procedure or variable described later in the same file,
     the keyword %external may be omitted.
  An %external variable has all the properties of an %own variable, but
  is declared with the keyword %own replaced by %external.
  Examples:
           %external %integer CHOICE = 6, WAIT = -5
           %external %real %array MEAN(-6:6) = 2.7(5),0.3,1.5(*)
  External variables can be declared in an external file or in a normal
  program file, wherever other declarations are valid.  They are
  normally declared in the outer block of an external file.
  Note that external variables may be initialised, like %own variables,
  when they are declared, but not when they are specified in an
  %external ... %spec statement.
Example of an external file
           %external %integer IN=0, OUT=0
           %routine GET(%integername SYM)
              READSYMBOL(SYM)
              IN = IN+1
           %end
           %routine PUT(%integer SYM)
              PRINTSYMBOL(SYM)
              OUT = OUT+1
           %end
           %external %routine PROCESS
              %integer CH
               :
              GET(CH) %until CH='*'
               :
              PUT(CH)
               :
           %end {Of PROCESS}
           %end %of %file
     A program making use of the external file:
           %begin
              %external %routine %spec PROCESS
              %external %integer %spec IN, OUT
              %integer DATA, DMAX
               :
              %for DATA = 1,1,DMAX %cycle
                 IN = 0; OUT = 0
                 PROCESS
                 PRINTSTRING("Calculation no.")
                 WRITE(IN,1)
                 PRINTSTRING(" characters in;")
                 WRITE(OUT,1)
                 PRINTSTRING(" characters out.")
                 NEWLINE
              %repeat
               :
               :
           %end %of %program
%alias
  Any identifier given in an %external declaration or specification may
  be followed by %alias string constant, where the string constant
  specifies the string to be used for external linkage.  This has no
  effect on the use of the identifier within the program.
  Examples:
           %external %long %real %fn %spec EIGENVALUE %alias %c
                           "D#REFEIGEN$" (%long %real %array %name MAT)
           %external %integer RESULT %alias "ICL9CERETURN" = 4
Executable Statements
In IMP80 a condition can form part of any executable statement.
Conditions are therefore described before the various types of
executable statement.
Conditions
The commonest form of simple condition in IMP80 is made up of two
expressions separated by a comparator.  The expressions are evaluated
and compared.  The condition is true if the relation specified by the
comparator holds.  The expressions must yield values of the same type.
Complete records cannot be compared.
Examples:
        J = 0
        A = "END"
        X+23.7 <= F(J+2,Y) - 2*Z/P
        REF == K
The comparators are:
        =                 is equal to
        # (or \= or <>)   is not equal to
        <                 is less than
        <=                is less than or equal to
        >                 is greater than
        >=                is greater than or equal to
        ==                refers to the same variable as
        ## (or \==)       does not refer to the same variable as
In the case of the last two comparators, == and ##, the items being
compared are references to variables, which must be of identical type.
The condition is true if the addresses of the variables referred to are
equal (==) or not equal (##). (Note that the address of a reference
variable is the address of the variable to which it refers: see Section
6.1.2)
{EMAS IMP80: the == and ## comparators cannot be used to compare
references to arrays.}
The other forms of simple condition are as follows:
a) <expression> <comparator> <expression> <comparator> <expression>
  This is known as a double-sided condition.
  Example:
           A+B <= C+D < E+F
     This condition is true if A+B <= C+D  and  C+D < E+F .
  The third expression is only evaluated if the condition between the
  first two expressions is true.
  The comparators == and ## (or \==) may not be used in double-sided
  conditions.
b) String resolution
  Example:
           A -> B.(C).D
  The resolution is attempted.  If it succeeds the condition is true AND
  the resolution is performed.  If it fails the condition is false, but
  no event is signalled.
c) Compound condition (see below) enclosed in brackets
  Example:
           (A>0 %or B<=0)
d) Any of the above forms preceded by the keyword %not
  The effect of preceding a simple condition with %not is to reverse the
  truth value of the condition.
  Examples:
           %not A <= 0   (equivalent to A > 0)
           %not 23 <= I+J <= 99
Compound conditions can be produced by combining simple conditions using
the keywords %and and %or:
        <simple condition> %and <simple condition> %and ....
        <simple condition> %or <simple condition> %or ......
Examples:
        A+B <= C+D %and C+D < E+F
        I = 20 %or A -> B.("..").C %or X > Y >= Z+3.47
It is not valid to combine %and and %or, as in
        X < Y %and B = C %or D = 24
However a compound condition enclosed in brackets is treated as a form
of simple condition (see above).  Thus
        (X < Y %and B = C) %or D = 24
is valid.  Note that the form
        %not (X < Y %and B = C)
is also permitted.
By combining %and and %or and brackets, conditions of arbitrary
complexity can be produced:
Example:
        (A <= B %or C == D) %and S -> ("Jim").T %and %c
           (X <= Y <= Z %or (P_K = 23 %and Q < 0))
The testing of conditions proceeds from left to right, simple condition
by simple condition, terminating any clause as soon as an inevitable
outcome for the clause has been established.  Thus, in the example
above, if A <= B were true then C == D would not be evaluated; if A <= B
were false and C == D were false then the remainder of the condition
would not be evaluated.
Example:
        A = 0 %or B/A = C
  If the variable A has the value 0 the whole condition will be true
  without B/A = C being tested.
        B/A = C %or A = 0
  In this case an event will be signalled ("overflow") if variable A has
  the value 0.
Instructions
An <instruction> is an imperative statement which may be made
conditional.  The following IMP80 statement types are instructions:
        statement      described in
        assignment     Section 2
        routine call   Section 3.2
        %monitor       Section 4.2.4
        %signal %event Section 3.1
        %return        Section 3.2
        %result=       Section 3.2
        jump           Section 4.2.1
        %stop          Section 4.2.3
        %exit          Section 4.4.3
        %continue      Section 4.4.3
In addition, a <compound instruction> can be formed by use of the
keyword %and:
        <instruction> %and <instruction> %and ......
Example:
        X = 23 %and %continue
The last of the series of instructions in a compound instruction can be
any of those listed above; the other component instructions can only be
assignments, routine calls or %monitor statements.
Conditional instructions
  Instructions without conditions are called unconditional.  An
  unconditional instruction can be made conditional as follows:
           %if <condition> %then <unconditional instruction>
     or
           %unless <condition> %then <unconditional instruction>
  In the first form, the instruction is executed if the condition is
  true; in the second, the instruction is not executed if the condition
  is true.  If the instruction is not executed, nothing is done.
  Examples:
           %if 0 < I <= 9 %and K > 0 %then B(I) = K
           %unless J = I %then A(I,J) = 0
  %if and %unless statements can be elaborated to allow specification of
  an alternative instruction, to be executed if the first one is not:
           %if <condition> %then <unconditional instr> %else <instr>
           %unless <condition> %then <unconditional instr> %else <instr>
  Example:
           %if X < 47.2 %then Y = Z+3 %else Y = 0.0
  Note that the first instruction in these elaborated %if and %unless
  statements must be unconditional but the second instruction can be
  conditional.
  Example:
           %if STATE < 0 %then ERROR = IN %else %c
              %if STATE = 0 %then ERROR = CALCULATION %else ERROR = OUT
  The simple forms of %if and %unless statements (i.e. those without the
  %else) can be made simpler still:
           <unconditional instruction> %if <condition>
           <unconditional instruction> %unless <condition>
  Examples:
           B(I) = K %if 0 <= I <= 9 %and K>0
           A(I,J) = 0 %unless J = I
Labels and jumps
  Any statement, excluding declarations, may be given one or more
  labels.  A label is an identifier but it is not declared; however, it
  does have to be distinct from other local identifiers.
  {EMAS IMP80: labels do not have to be distinct from other
  identifiers.}
  Each label is located immediately to the left of the statement to
  which it refers, followed by a colon.
  Examples:
           NEXT: P = P+1 %if P < 0
           ERR1: ERR2: FAULTS = FAULTS+1
  Control is passed to a labelled statement when a jump instruction of
  the form
           -> <label>
  is executed.
  Examples:
           ->NEXT
           %if DIVISOR = 0 %then ->ERR1
  As indicated, jump instructions can be made conditional (as can all
  instructions).
Switches
  A set of labels, known as a switch, may be declared in a manner
  similar to a one-dimensional array, but using the keyword %switch.
  The switch must have bounds which can be determined at compile-time
  (i.e. constants, or expressions comprising constants and variables of
  type %constant).
  Examples:
           %switch TYPE(4:9)
           %switch S1, S2(1:10), S3('A':'Z')
  Once declared, switch labels may be located in the same way as simple
  labels, the particular label of the switch being specified by an
  integer constant.
  Example:
           %switch SW(4:9)
           %constant %integer FAULTY = 6
            :
            :
           SW(4): CHECK VALUE(1)
            :
           SW(FAULTY): ERROR FLAG = 1
            :
            :
           LAST: SW(9): ! All finished.
            :
  Control is passed to a statement labelled by a switch label when a
  jump instruction of the form
           -> <switch> ( <integer expression> )
  is executed.
  Not all of the labels in the switch need be located; in the example
  above SW(5), SW(7) and SW(8) are not, and do not need to be elsewhere
  in the program.  An event is signalled if an attempt is made to jump
  to an undefined switch label.
  Instead of an integer constant, an asterisk (*) may be used when
  locating a switch label.  This has the effect of defining any labels
  in the switch not defined elsewhere in the program block.
  Example:
           %switch LET('a':'z')
            :
            :
           LET('a'):LET('e'):LET('i'):LET('o'):LET('u'):
           ! Deal with vowels here.
            :
            :
           LET(*): ! All the rest, i.e. consonants
            :
  Notes
   * A label can appear on a line without being followed by a statement.
     This is sometimes done to improve readability.  Strictly, the label
     is labelling a null statement.
     Example:
              LAST STAGE:
                    %if Y < 23.7 %and ....
   * The use of both types of label is limited to the block in which
     they are defined, excluding any blocks described therein.  That is,
     labels cannot be global to a block and therefore it is not possible
     to jump into or out of a block.
   * The identifiers used for labels must be distinct from other local
     identifiers.
     {EMAS IMP80: labels do not have to be distinct.}
   * The effect of jumping into a %for loop (see Section 4.4) is
     undefined.
%stop
  This instruction causes the execution of the program to be terminated.
  {IMP77: %stop is event 0 and can therefore be trapped.}
%monitor
  This instruction passes control to a run-time diagnostic package which
  then generates a trace of the state of the program.  In
  implementations of IMP80 without a run-time diagnostic package
  %monitor is a null instruction.  In some implementations the amount of
  trace information can be controlled by the programmer.
  Following the trace the program resumes at the point where it left
  off.
%start/%finish
%start/%finish statements are used to make the execution of a group of
statements conditional.  The most general type of conditional group is a
sequence of statements of the form:
        %if <condition 1> %then %start
           statements to be executed if <condition 1> is true
        %finish %else %if <condition 2> %then %start
           statements to be executed if <condition 1> is false
           and <condition 2> is true
        %finish %else %if <condition 3> %then %start
         :
         :
        %finish %else %start
           statements to be executed if all the previous
           conditions are false
        %finish
Notes
* "%if ... %start" and "%finish %else ... %start" are complete
  statements in their own right and as such must be terminated by a
  newline or semicolon.
* Each %start and the next %finish effectively bracket the statements
  between them, which are all controlled by the same set of conditions.
* Any or all of the statements containing the keyword %else may be
  omitted.  When they are all omitted, the form becomes:
        %if <condition 1> %then %start
           statements to be executed if
           <condition 1> is true
        %finish
* Other simple forms:
     a)  %if <condition 1> %then %start
           statements to be executed if
           <condition 1> is true
        %finish %else %start
           statements to be executed if
           <condition 1> is false
        %finish
     b)  %if <condition 1> %then %start
           statements to be executed if
           <condition 1> is true
        %finish %else %if <condition 2> %then %start
           statements to be executed if
           <condition 1> is false and
           <condition 2> is true
        %finish
* %start/%finish groups may be nested to any depth.
* %then %start may be elided into %start.
* If a %start and the next %finish enclose only one instruction then the
  complete %start...%finish sequence can be replaced by that
  instruction.
  Example:
            :
           %else %if K = 0 %then %start
              X = 4*SQRT(Y**2 + Z**2 - 4)
           %finish %else %start
            :
     can be given as
            :
           %else %if K = 0 %then X=4*SQRT(Y**2 + Z**2 - 4) %else %start
            :
* The keyword %if may always be replaced by %unless, with the effect of
  negating the subsequent condition.  Thus the following two statements
  are equivalent:
           %if X = 0 %then Y = 1 %else Z = -1
           %unless X = 0 %then Z = -1 %else Y = 1
* Where a small group of unconditional instructions is to be made
  conditional, a compound instruction (Section 4.2) can be used instead
  of a %start/%finish construction.
  Example:
           %if K<0 %then X = 4 %and %return
     is equivalent to
           %if K<0 %start
              X = 4; %return
           %finish
%cycle/%repeat
%cycle and %repeat statements are used to bracket statements which are
to be repeated.  In the simplest case, a group of statements may be
repeated indefinitely by enclosing them between the statements %cycle
and %repeat.
Example:
        %cycle
           READ DATA; ! Get next set of data
           PROCESS DATA; ! Carry out calculation
           OUTPUT DATA; ! Print results
        %repeat
The statements between a %cycle statement and the corresponding %repeat
statement are known as the cycle body.  %cycle/%repeat groups can be
nested to any depth, i.e. a cycle body can contain further
%cycle/%repeat groups.
Conditional repetition
  The number of times the cycle body is executed can be controlled by
  modifying the %cycle statement or the %repeat statement.
  a)  %while <condition> %cycle
      :
      :
     %repeat
     The specified condition is tested before each execution of the
     cycle body.  If the condition is true the cycle body is executed;
     otherwise control is passed to the statement following the matching
     %repeat.
     The cycle body will be executed zero or more times.
  b)  %for <control variable> = <init> , <inc> , <final> %cycle
      :
      :
     %repeat
     where <control variable> is a variable of type %integer, and
     <init>, <inc> and <final> are all integer expressions.
     This is a special form of %while...%cycle, in which an integer
     variable takes a series of regularly spaced values in a specified
     range.  Each value corresponds to an execution of the cycle body.
     Example:
              %for I = 1,1,10 %cycle
                 A(I) = I+1
                 B(I) = 0
              %repeat
        The cycle body is executed 10 times, the control variable (I)
        taking the values 1, 2, 3, ..., 9, 10 in succession.
     In detail, the execution of a %for loop entails the following
     stages.  A test is first made that <inc> is non-zero and that
     (<final> - <init>) is exactly divisible by <inc>.  An event is
     signalled if it is not.  This test may be carried out at
     compile-time, if the three expressions permit; a compile-time fault
     may then be given.
     If the test is successful, the total number of times that the cycle
     body could be executed is calculated; this is
     ((<final> - <init>) // <inc>) + 1.
     {EMAS IMP80: if this number is less than or equal to zero, the
     control variable is set to be unassigned and control is passed to
     the statement following the matching %repeat.  Otherwise the
     control variable is assigned the value of <init> and the cycle body
     is executed.  When the matching %repeat is reached the value of the
     control variable is tested, and if it is equal to <final>, control
     is passed to the statement following the %repeat.  Otherwise the
     control variable has <inc> added to its value and the cycle body is
     executed again.}
     {IMP77: an event is signalled if the total number of times that the
     cycle body could be executed is less than 0.  Otherwise the control
     variable is assigned the value <init> - <inc>.  Before each
     execution of the cycle body the value of the control variable is
     compared with <final>.  If they are equal control is passed to the
     statement following the matching %repeat; otherwise <inc> is added
     to the control variable and the cycle body is executed.  It follows
     that if the cycle execution is terminated by means of this test,
     the control variable will thereafter be equal to <final> in all
     cases.}
     It follows that the cycle body will be executed zero or more times.
     Notes
      * <inc> can be negative.
      * The control variable must be of type %integer - a byte integer
        or long integer etc. is not allowed.
      * The effect of jumping into a %for loop is undefined.
      * The effect of changing the value of the control variable within
        the cycle body is undefined.
      * The three expressions <init>, <inc> and <final> are evaluated
        once only, before the cycle body is executed.  It is
        permissible, within the cycle body, to change the value of any
        variable appearing in one of these expressions, but it has no
        effect on the execution of the %for loop.
  c)  %cycle
      :
      :
     %repeat %until <condition>
     After each execution of the cycle body the condition is tested.  If
     false the cycle body is executed again.
     %until loops always execute the cycle body at least once (cf.
     %while loops).
  {IMP77:
  d)  Any of the statements described above containing the keyword
     %cycle can be matched with any of the statements containing the
     keyword %repeat.
     Example:
              %while I<=100 %cycle
                  :
                  :
              %repeat %until J=4
     The cycle-termination test implied by each statement is made when
     the statement is executed.}
Simple forms of loop
  If the cycle body comprises only one instruction (which can be a
  compound instruction), the loop may be written in the form:
           <instruction> <loop clause>
     i.e.
           <instruction> %while <condition>
           <instruction> %for <control> = <init>, <inc>, <final>
           <instruction> %until <condition>
  Examples:
           SKIPSYMBOL %and SP = SP+1 %while NEXTSYMBOL = ' '
           A(J) = 0 %for J = 1,1,20
           READSYMBOL(S) %until S = NL
%exit and %continue
  Two instructions are provided to control the execution of a cycle from
  within the cycle body.
  a) %exit
     This instruction causes execution of the cycle to be terminated.
     Control is passed to the statement following the matching %repeat.
  b) %continue
     This instruction causes control to be passed to the matching
     %repeat.  The effect is thus to terminate this execution of the
     cycle body, but not of the complete cycle.
  Notes
   * %exit and %continue can only be used within %cycle/%repeat loops.
   * %exit and %continue are instructions, and can thus be made
     conditional.
     Examples:
              %continue %if P_J = 0
              X = 5 %and %exit %if CPU TIME > 50
   * Within nested loops, %continue and %exit operate with respect to
     the innermost loop in which they are contained; i.e. control is
     passed to the next %repeat or to the statement following it,
     respectively.
Input/Output Facilities
Input/Output (I/O) facilities are provided to enable programs to read
data from files or input devices, and send data to files or output
devices.  These facilities take the form of standard procedures,
described below.  (As explained in Section 7, standard procedures do not
require to be specified before use; they are defined implicitly).  All
the procedures described refer to logical I/O channels to which numbers
in the range 0-99 have been assigned.  In some implementations certain
channel numbers are reserved for system use.
{EMAS IMP80: channels 0 and 81-99 are reserved for system defined
devices.  Channel numbers in the range 1-80 can be used for purposes
defined by the user, subject only to the rule that a channel number can
only refer to one channel at a time.}
{IMP77: input channel n and output channel n are logically distinct.}
Each implementation of the language provides facilities for linking
these logical channels to particular files or I/O devices; refer to the
relevant User Guide for information on this subject.
The primary I/O facilities in IMP use character information, that is,
information that can be represented as sequences of characters.
All the routines and functions use an Internal Character Code based on
the ISO Code for the interchange of data (see Section 1).  Some
implementations of IMP may use other codes.
Facilities are also provided to handle binary information, as a direct
copy of the representation of values in the computer store.  Two types
of binary I/O are provided: Sequential Access for use when data is
accessed (read or written) in the order in which it is held in the
store, and Direct Access for use when data is accessed randomly from the
store.  All the binary I/O routines and functions are explicit, and so
must be declared in each program in which they are used (see Section
5.2).
Character I/O
All character handling routines and functions operate with respect to
either the currently selected INPUT STREAM or the currently selected
OUTPUT STREAM.  An input or output stream corresponds to a logical I/O
channel (described above), and is referenced by the appropriate channel
number.  All character I/O streams act upon continuous streams of ISO
characters.  Thus an input stream consists of a continuous stream of
characters with an associated pointer which at any instant points to the
next character to be input.  Similarly, an output stream is able to
accept a continuous stream of ISO characters.  Naturally, there has to
be some implementation-dependent mechanism for transferring these
characters to and from files or physical devices, but this is hidden
from the user.
On entry to a program, default streams are selected for input and
output.  At any point in the program it is possible to redirect
character input or output by a call of the standard procedure
              %routine SELECT INPUT(%integer I)
           or
              %routine SELECT OUTPUT(%integer I)
Each of these routines takes one integer parameter, the value of the
logical channel number corresponding to the stream being selected.  An
event is signalled if the specified channel does not correspond to a
valid input or output stream, as appropriate.
Examples:
        SELECT OUTPUT(3)
        SELECT INPUT(I+17)
After a call of SELECT INPUT, all calls of character input routines will
operate on the selected input stream, until another call of SELECT INPUT
is made or the end of the program is reached.  The same rule applies to
SELECT OUTPUT and character output routines and functions.
In some implementations, both input and output characters may be
buffered by the operating system into lines.  This has the following
unfortunate side effects when using SELECT INPUT and SELECT OUTPUT.  If
an input stream is reselected in a program, the first character read
after reselection will be the first character in the line following the
last line accessed, thus losing any characters not read from the
previous line.  When an output stream is selected, a newline character
is output to the current output stream if there is anything in the line
buffer, before the selection takes place.
Input of character data
  The procedures described below operate on the currently selected input
  stream.  In all cases an event is signalled if an attempt is made to
  read beyond the end of the input stream.
  {EMAS IMP80: the following ISO codes (see Section 1) are ignored by
  these procedures: DEL (value 127), and all codes with values less than
  32 apart from NL (10), EM (25) and SUB (26).  If it is desired to read
  these codes, rather than ignore them, then the procedures READ CH and
  NEXT CH (described below) should be used.}
  %routine READ SYMBOL(%name I)
     This routine transfers the internal value of the next symbol from
     the current input stream into an integer variable of any
     permissible length (byte, long, etc).
     Example:
              READ SYMBOL(IN); ! IN is an %integer variable.
     {IMP77: the actual parameter supplied can be of type %string, of
     any length, in which case a single character is read and held
     internally as a one-character string.}
  %integer %function NEXT SYMBOL
     This integer function returns the internal value of the next symbol
     on the current input stream.  It does not move the pointer to the
     stream, so the next call of this or any other character input
     routine will access the same character again.
     Example:
              %if NEXT SYMBOL = 'a' %then .....
  %routine SKIP SYMBOL
     This routine moves the pointer to the current input stream along
     one symbol without transferring any information to store.  In the
     following example, SKIP SYMBOL and NEXTSYMBOL are used together to
     skip over a series of space characters.
     Example:
              SKIPSYMBOL %while NEXTSYMBOL = ' '
  %routine READ STRING(%string(*)%name S)
     This routine is used to read a string into the string variable
     location specified by the parameter.  The string should be in the
     format of an IMP80 string constant.  Any spaces and newline
     characters which precede the double quote character at the start of
     the string will be ignored.  An event will occur if the input
     stream does not contain a string constant, or if the input string
     overflows the location specified by the parameter.
     {IMP77: READ STRING is not provided.  Instead the routine READ,
     described below, is extended to accept a string name parameter and
     to read a string into it, as described above.}
  %routine READ ITEM(%string(*)%name S)
     This routine reads the next symbol from the current input stream
     and stores it as a string of length 1 in the string variable
     specified by the parameter.
     {IMP77: READ ITEM is not provided.  Instead the routine READ SYMBOL
     has been extended, as described above, to accept a string name
     parameter.}
  %string(*)%function NEXT ITEM
     This function reads the next symbol from the currently selected
     input stream without moving the pointer to the input stream (as
     with the function NEXTSYMBOL).  The symbol is returned as a string
     of length 1.
     {IMP77: NEXT ITEM is not provided.}
  {EMAS IMP80: the following '...CH' procedures are provided.  They have
  the same effect as their '...SYMBOL' counterparts described above,
  except that they do not ignore any ISO characters.
  %routine READ CH(%integer %name I)
     This routine transfers the internal value of the next symbol from
     the current input stream into an %integer variable.  %All
     characters of the ISO character set are passed back by READ CH.
     READ CH will pass back EM (the end message character) when
     end-of-file is reached and will signal an event if further reads on
     this file are requested.
  %routine NEXT CH
     This routine reads the next character from the input stream without
     moving the pointer to the stream (as with NEXTSYMBOL).  All
     characters of the ISO character set, including End Message
     character (EM), are passed back by NEXT CH. }
Input of numeric data
  %routine READ(%name I)
     This routine is used to read numeric data into an arithmetic
     variable.  The single parameter should be of type %integer or %real
     and of any length.  The numbers being read should be written as
     described for decimal constants, except that no space characters
     may be included in the constants.  Any space or newline characters
     which precede the number will be skipped by the routine; thus
     spaces and newlines can be freely used as separators between
     numbers.  The number is terminated by any character in the input
     stream other than digit, +, -, . or @.  On return from READ, the
     input stream pointer will be pointing to the character immediately
     following the number just read.
     Note that if an attempt is made to read either a real number, or a
     number with an exponent, into an integer variable, then the reading
     will stop at the decimal point or the "@" symbol, but no event will
     be signalled.  However, an event will be signalled if the first
     character of a number is neither a sign, nor a decimal digit nor a
     decimal point.  The same event will occur if the initial character
     is a decimal point but the parameter passed to READ is an integer.
     The range of values which can be read depends on the given
     parameter and on the implementation.
     {IMP77: the parameter to READ can be the name of a string, of any
     length, in which case the procedure behaves exactly like READ
     STRING, described above.}
Output of character data
  A number of procedures are provided to write individual characters or
  sequences of characters to the currently selected output stream.
  Note that when an interactive terminal is used, the symbols will only
  be transmitted when a 'trigger' character, normally newline (NL) or
  form feed (FF), is sent to the output stream.
  The basic character output procedure is PRINT SYMBOL; most of the
  other character output procedures operate via this procedure.
  %routine PRINT SYMBOL(%integer I)
     The parameter passed to this routine must be an integer expression
     which is evaluated by the routine.  Any character whose value lies
     in the range 0-255 is transmitted to the output stream.
     {EMAS IMP80: the parameter passed to this routine must be an
     integer expression.  The expression is evaluated and the least
     significant 7 bits only are used by the routine.  If the value of
     the expression corresponds to that of a symbol which could be read
     by READ SYMBOL (see above) then it is sent to the output stream; if
     not, the character SUB (code value 26) is sent.}
     Examples:
              PRINTSYMBOL('A')
              PRINTSYMBOL(I+J+32)
  %routine PRINT STRING(%string(255) S)
     This routine transmits to the currently selected output stream the
     string expression specified by the string parameter.
     {EMAS IMP80: PRINT STRING operates effectively as a series of PRINT
     SYMBOL calls.  Thus certain characters will be replaced by SUB, as
     explained above.}
  {EMAS IMP80:
  %routine PRINT CH(%integer I)
     This routine behaves exactly like the standard version of PRINT
     SYMBOL, i.e. any character whose code value lies in the range 0-255
     is transmitted to the output stream. }
  There are a few other standard procedures provided to simplify the
  output of text.
  SPACE          transmits one space character to the currently selected
                 output stream
  SPACES(N)      transmits N space characters to the currently selected
                 output stream, where N is an integer expression.
  NEWLINE        transmits one newline character to the currently
                 selected output stream.
  NEWLINES(N)    transmits N newline characters to the currently
                 selected output stream, where N is an integer
                 expression.
  NEW PAGE       transmits one Form Feed (FF) character to the currently
                 selected output stream.
                 {IMP77: NEW PAGE is not provided.}
Output of Numeric Data
  Three routines are provided to allow output of numeric information.
  WRITE is used to transmit the value of integer expressions; PRINT and
  PRINTFL transmit the values of real expressions in floating point
  form.
  %routine WRITE(%integer I, J)
     This routine transmits the value of the integer expression I to the
     currently selected output stream.  The second parameter specifies
     the number of positions to be used.  To simplify the alignment of
     positive and negative numbers, an additional position is allowed
     for a sign, but the sign is only printed in the case of negative
     numbers.  If the number to be printed needs more positions than are
     specified by the second parameter, then more positions are used.
     Examples:
              WRITE(I, 4)
              WRITE(TOTAL+SUM+ROW(I), 6)
              WRITE(SNAP, POS+4)
     {IMP77: the total number of print positions to be used is defined
     by the modulus of the second parameter.  If this parameter is
     negative, no space character is output before a positive value.}
  %routine PRINT(%long %real X, %integer I, J)
     This routine transmits to the currently selected output stream the
     value of the real expression specified by the first parameter.  The
     second and third parameters should be integer expressions
     specifying the number of places to be allowed before and after the
     decimal point.  If the integer part needs more positions than are
     specified by the parameter, then more positions will be taken.  One
     position is allowed for a sign which is printed only in the
     negative case.  If necessary, the fractional part will be rounded.
     Examples:
              PRINT(A, 2, 3)
              PRINT(COS(A-B), 1, 10)
     {IMP77: The second parameter is interpreted in the same way as the
     second parameter of WRITE (described above).}
  %routine PRINT FL(%longreal X, %integer I)
     This routine transmits to the currently selected output stream the
     value of the real expression specified by the first parameter.  The
     second parameter specifies the number of places to be allowed after
     the decimal point.  The printed number takes up the specified
     number of places, plus 7 additional places.
     Example:
              PRINT FL(X,4)
     If X has the value 17.63584, this would be printed as  1.7636@ 1.
     The number is standardised in the range 1 <= X < 10.  One position
     each is allowed for signs for mantissa and exponent; in each case
     the sign is only printed when negative.
Closing streams
  All input and output streams are closed automatically when the program
  terminates.  However, to close a stream during the running of a
  program, a call of the appropriate routine is necessary.
  {EMAS IMP80:
           %routine CLOSE STREAM(%integer I)}
  The parameter must be an integer expression which will evaluate to the
  number of the stream to be closed.  The currently selected input or
  output stream cannot be closed, and any attempt to do so will cause an
  event to be signalled.
  {IMP77:
           %routine CLOSE INPUT
           %ROUTINE CLOSE OUTPUT
  In each case the routine closes the CURRENT stream, input or output as
  appropriate.  The input or output stream then becomes null, until
  another channel is selected by use of SELECT INPUT or SELECT OUTPUT.
           RESET INPUT
  This routine resets the current input stream to the start of the file.
           RESET OUTPUT
  This routine throws away all output on the current stream. }
  If a stream is closed and then reselected for input, the pointer to
  the stream will be positioned at the start of the file.  Thus a file
  can be re-read, or a file written earlier in the program can be read.
  In the same way, if a stream is closed and then reselected for output,
  the pointer to the file is left at the start of the file.  Thus any
  information in the file at the time it is reselected will be
  overwritten by data transmitted to the file after reselection.
Binary I/O
Binary I/O facilities are provided by procedures which must be specified
explicitly.  They do not form part of the IMP80 language, but a list and
brief explanation is given in Appendix B for some implementations.  A
more detailed description of the procedures can be found in the
appropriate User Guide or Library Manual.
Store Mapping
A variable declared in an IMP80 program is allocated storage on the
stack (Section 3).  It is possible in IMP80 to give an alternative name
to such a variable.  It is also possible to refer to an arbitrary store
location, not necessarily within the stack area, and operate on it as
though it held a variable of a specified type.
"Store mapping" is the name given to this technique.  It is useful for
the following reasons:
   * to enable entities not declared within a program to be accessed and
     operated on by the program, without recourse to machine code or
     other machine specific features
   * to save space in main store (see first example below)
   * to access a variable both as declared, and as a set of
     sub-variables; for example, a variable of type %integer can be
     accessed as several separate variables of type %byte %integer
   * to access an array element as a simple variable, with consequent
     saving of machine time
   * to improve the clarity of a program
The store mapping facilities in IMP80 are provided by means of several
components:
   * reference variables
   * user-written mapping functions
   * the ADDR function
   * the standard mapping functions
Reference variables & mapping fns
  Two of the fundamental entities from which programs are constructed
  are constants and variables (Section 2).  A variable comprises an
  identifier (its name), and a type which specifies what type of value
  it may have.  Its value is a constant, or a reference to a variable.
  Thus, the value of a variable of type %integer is an integer constant,
  while the value of a variable of type %integer %name is a reference to
  an integer variable.
  A function (Section 3) is similar to a variable in that it has an
  identifier, and a type which specifies what sort of value it returns.
  A mapping function (or map) is analogous to a reference variable in
  that its type (the type of the value that it returns) is a reference
  to a variable; for this reason maps are sometimes called "name
  functions".
  The difference between a variable and a function, of course, is that a
  variable has its value stored while a function computes its result.
Examples
  1) Use of a reference variable with a map.
     In this program, a two-dimensional array is accessed by means of a
     map, because the array is symmetrical: i.e. X(i,j) = X(j,i) for all
     valid i and j.  Thus to save storage space only the values of
     X(i,j) with i >= j are stored.  By keeping only these values in a
     one-dimensional array A - which is global to the map - we can make
     economical use of store without losing the symmetrical appearance
     of the array X.
     The array is first assigned values and then various references to
     it involving %integer ALPHA and %integer %name BRAVO are made.
%begin
%integer %array A(1:210)
%integer ALPHA, I, J
%integer %name BRAVO
%integer %map X(%integer I,J)
   %signal %event 6 %unless 1<=I<=20 %and 1<=J<=20; ! Array bound check.
   %result == A(I*(I-1)/2 + J) %if I>J
   %result == A(J*(J-1)/2 + I)
%end
%for I = 1,1,20 %cycle
   X(I,J) = I\\2 + 2*I*J + J\\2 %for J = 1,1,I
%repeat
ALPHA = X(17,10)    {ALPHA assigned the value of the variable returned
                    {by the call X(17,10) of map X.
BRAVO == ALPHA      {BRAVO made to refer to ALPHA (thus BRAVO is now
                    {synonymous with ALPHA).
BRAVO = 4           {BRAVO (i.e. ALPHA) assigned the value 4.
BRAVO == X(16,9)    {BRAVO made to refer to the variable returned by
                    {the call X(16,9) of map X.
BRAVO = 4           {BRAVO (i.e. the variable it currently refers to)
                    {assigned the value 4.
ALPHA = BRAVO       {ALPHA assigned the value of the variable to which
                    {BRAVO currently refers. This value is 4.
X(6,15) = 17        {The variable to which reference is returned by
                    {the call X(6,15) of map X is assigned the
                    {value 17.
ALPHA = X(15,6)     {ALPHA set to the value of the variable returned
                    {by the call X(15,6) of the map X. From the
                    {previous statement, and bearing in mind the
                    {symmetry of X, this value is 17.
 :
%end %of %program
  2) Reference variable used as a procedure parameter
     When a procedure is called, its formal parameters - in the example
     below SIZE and STATUS - are assigned values according to their
     types and to the actual parameters used in the call.  SIZE is of
     type %integer and so has the current value of TOTAL assigned to it;
     STATUS is of type %integer %name and so is "pointed at" the integer
     variable RETURN.
     This example is included because procedure %name-type parameters
     provide a method for renaming variables, and as such represent a
     common application of store mapping techniques.
           %routine %spec TEST(%integer SIZE, %integer %name STATUS)
           %integer TOTAL, RETURN
            :
           TOTAL = 3
           TEST(TOTAL, RETURN)
           ! TEST called: formal parameters assigned values.
            :
  3) Mapping a direct access file
     The following map enables the programmer to treat a direct access
     file held on backing store as though it were a real array with
     declaration %real %array FILE(1:NBLOCK, 0:255), where NBLOCK is the
     number of blocks in the file.
     In use, the only difference from a normal array is that a closing
     call <real var> = FILE(0,n)  must be made.
     In this implementation of IMP80, real variables are 32-bit
     entities, and each block of a direct access file consists of 1024
     8-bit bytes.
     It is assumed that the direct access file has been associated with
     channel number 1 prior to the execution of this program.
     Note that when a virtual memory operating system is being used, a
     simpler and better method for mapping arrays onto files may be
     available - see later example.
%real %map FILE(%integer BLOCK, ELEMENT)
   ! The external routines specified below relate to the use of direct-
   ! access (DA) files.  They are the standard routines provided in most
   ! implementations of IMP80, but do not form part of the language
   ! definition. Further details for some implementations are given in
   ! Appendix B.
   %externalroutinespec OPEN DA(%integer CHANNEL)
   %externalroutinespec CLOSE DA(%integer CHANNEL)
   %externalroutinespec READ DA(%integer CHANNEL, %integername BLOCK,
                                %name START, FINISH)
   %externalroutinespec WRITE DA(%integer CHANNEL, %integername BLOCK,
                                 %name START, FINISH)
   %constant %integer NO=0, YES=1
   %own %integer CURRENT BLOCK = 0, BLOCK CHANGED = NO
   %own %integer LAST ELEMENT
   %own %real LAST VALUE
   %own %real %array BUF(0:255) = 0(256)
   %unless BLOCK>0 %and 0<=ELEMENT<=255 %start
      ! Could be an error, or the closing call.
      ! In either case, tidy up and close the file.
      WRITE DA(1, CURRENT BLOCK, BUF(0), BUF(255)) %if BLOCK CHANGED=YES
      CURRENT BLOCK = 0
      %if BLOCK=0 %start; ! Closing call.
         CLOSE DA(1)
         %result == BUF(0); ! Irrelevant in this case.
      %finish
      ! Error - parameters out of range.
      %signal %event 6
   %finish
   %if CURRENT BLOCK=0 %start; ! First call.
      OPEN DA(1)
      CURRENT BLOCK = BLOCK
      READ DA(1, CURRENT BLOCK, BUF(0), BUF(255))
   %finish %else %start; ! Not the first call.
      ! Has the value of the last element returned by the map been
      ! changed?  If so, note the fact so that the block is written back
      ! when no longer required by the map.
      BLOCK CHANGED = YES %if BUF(LAST ELEMENT) # LAST VALUE
      %if CURRENT BLOCK # BLOCK %start
         ! Block required differs from that currently held in BUF.
         ! Write currently held block back if it has been changed.
         WRITE DA(1,CURRENT BLOCK,BUF(0),BUF(255)) %if BLOCK CHANGED=YES
         ! Now get block required.
         CURRENT BLOCK = BLOCK
         READ DA(1, CURRENT BLOCK, BUF(0), BUF(255))
         BLOCK CHANGED = NO; ! Reset change flag.
      %finish
   %finish
   LAST ELEMENT = ELEMENT
   LAST VALUE = BUF(ELEMENT)
   %result == BUF(ELEMENT)
%end; ! Of %real %map FILE.
ADDR, standard mapping fns
     A variable has an identifier, a type and a value.  A fourth
     attribute of an accessible variable is its address.  This is a
     positive integer which uniquely specifies its location in the
     computer store.  The address of a variable can be obtained by use
     of the standard %integer %function ADDR.
     Example:
              %string(27) S
              %integer ADDRESS OF S
               :
              ADDRESS OF S = ADDR(S)
     The parameter of ADDR can be a variable or array element of any
     type; in the case of a reference variable parameter, ADDR returns
     the address of the variable to which it refers, not the address of
     the reference variable itself.
     Given a variable, it is thus possible to find its address.
     Conversely, given an address, it is possible to construct a
     variable of any type, subject to certain restrictions detailed
     below.  This is achieved by the use of the standard mapping
     functions.
     For each arithmetic and string type there is an associated standard
     map whose name is the same as that of the type; thus BYTE INTEGER,
     LONG REAL, STRING, etc.  A standard map has a single parameter of
     type %integer, which is an address; it returns a reference to a
     variable of the appropriate type, located at that address.
     There are also two special standard maps, ARRAY and RECORD.  They
     are described later in this Section.
     Example:
              %integer I, J
              %byte %integer %array B(0:3)
               :
              I = M'ABCD'
               :
              B(J) = BYTE INTEGER(ADDR(I)+J) %for J = 0,1,3
               :
        In this example the integer I has been unpacked into its four
        component byte integers (implementation using 32-bit integers
        assumed).
     Example:
              %byte %integer %array IN(0:80)
              %string(*)%name LINE
               :
              LINE == STRING(ADDR(IN(0)))
               :
        From this point onward the array can be referenced either as an
        array of bytes or as the string LINE.  Obviously the length byte
        of the string, normally the first byte of the string location,
        will have to be set to an appropriate value.
     There are a number of points to note about the use of the standard
     mapping functions:
      * They are efficient.
      * An address error will occur if the address passed to a standard
        mapping function is not aligned correctly with respect to the
        type of reference implied.
        On byte addressed machines, for example:
           SHORT INTEGER requires the address to be divisible by 2
           INTEGER (if 32-bit integers used) requires the address to be
                   divisible by 4
           REAL requires the address to be divisible by 4
        The maps for the longer arithmetic types may have corresponding
        requirements.
      * In Section 3 it was explained that variables declared in a
        program are allocated storage on the stack.  However, if the
        computer system being used enables an IMP80 program to obtain
        store addresses of items not on the stack (e.g. items within the
        operating system, or a connected file in a virtual memory
        system), then the standard mapping functions can be used to
        refer to them as variables.  This is an extremely powerful
        facility.
      * It is possible to cause a location in which, for example, a real
        constant is stored to be treated as an integer or a string, etc.
        While this facility may be useful on occasions it should be used
        with care, because: 1) misuse can lead to errors which are hard
        to diagnose; 2) it requires a knowledge of the precise format of
        data types and is necessarily implementation dependent; 3) the
        purpose of providing types in IMP80 is to enable checks to be
        made that dissimilar items of data are not being erroneously
        combined.  Use of the standard mapping functions merely to avoid
        such checks is bad programming practice.
ARRAY
{EMAS IMP80:
  There is only one standard mapping function, ARRAY, for all the array
  types.  It takes two parameters: an address, and an array format to
  specify the type of the array being referenced.  %array %format
  statements are analogous to %record %format statements in that they
  enable an identifier to be associated with a type and a structure.
  They can then be referenced by name in subsequent statements.
  ARRAY can only be used to assign to a reference variable of the
  appropriate array type.
  Example:
           %integer %array AONE(1:10 000)
           %integer %array %name ATWO
           %integer %array %format AFORM(1:100, 1:100)
            :
           ATWO == ARRAY(ADDR(AONE(1)), AFORM)
            :
           ATWO(27, 43) = 928
            :
     The %array %format statement is used to describe the
     characteristics of the array ATWO - i.e. the number of dimensions
     and bounds for each dimension.
  As an alternative to using an array format for the second parameter,
  the name of another array (of the appropriate type) can be used, if
  one with suitable characteristics has been declared and is in scope.
  In order to map record arrays, the second parameter of ARRAY must
  either be an existing record array variable, or a record array format.
  A record array format must specify both the dimensions of the array
  and the format of each (record) element of the array:
     %record (<record format>) %array %format <name> (<array dims>)
  Example:
     This example is of a record array format being used in an IMP80
     program running under the operating system EMAS.  The integer
     function SMADDR returns the address of a file connected in the
     virtual memory of the user process.
           %routine PAYCHECK(%integer CHANNEL, RECNO)
           %integer I, J, K, START, LENGTH
           %string(11) NAME
           %external %integer %fn %spec SMADDR(%integer CHANNEL,
                                               %integername L)
           %record %format PAYF (%string(11) SURNAME, %integer AGE, SEX,
                                   YEAR, %integerarray SALARY (1:12))
           ! Each record thus formatted contains 72 bytes.
           %record(PAYF)%array %format PAYAF(1:RECNO)
           %record(PAYF)%array %name PAY
            :
           ! Assume that a file was associated with channel CHANNEL,
           ! prior to the execution of the routine.
           START = SMADDR(CHANNEL,LENGTH); ! File now connected.
           %if LENGTH < 72*RECNO %start
              PRINTSTRING("File too small:")
              WRITE(LENGTH,1); NEWLINE
              PRINTSTRING("Must be at least")
              WRITE(72*RECNO,1); NEWLINE
              %return
           %finish
           PAY == ARRAY(START,PAYAF)
           ! Now record array name PAY has been mapped onto the file.
           ! Note that START was set by the SMADDR call.
            :
           NAME=PAY(I)_SURNAME
           %if PAY(I)_SALARY(J)>350 ....
            :
           PAY(K)_YEAR=1978
            :
           %end; ! Of %routine PAYCHECK. }
RECORD
  The main use of the standard map RECORD is to assign a reference to a
  record reference variable.  The format of the record in question is
  that of the reference variable on the left-hand side.
  Example:
           %record %format F(%integer A, B, C, %real D, %string(11) E)
           %record (F) R
           %record (F) %array RA(1:50)
           %record (F) %name N1, N2, N3
           %integer ADDRESS
            :
           ADDRESS = address of some store location
            :
           N1 == R;! Record name N1 now synonymous with record R.
           N2 == RA(20);! Record name N1 now synonymous with element 20
                        ! of record array RA.
           N3 == RECORD(ADDRESS);! Record name N3 now synonymous with a
                                 ! record of format F located at
                                 ! address ADDRESS.
            :
  Example:
           %integer J
           %integer %array II(1:100)
           %record %format A(%byte %integer I, J, K, L)
           %record %format B(%half %integer P, Q)
                             ! Implementation-dependent type
           %record(A)%name X
           %record(B)%name Y
            :
           X == RECORD(ADDR(II(J)))
           Y == RECORD(ADDR(II(J)))
            :
     Now, for example:
        X_I is a reference to the leftmost byte of II(J)
        Y_P is a reference to the leftmost half-word of II(J)
     This example could have used an alternative record format:
           %integer J
           %integer %array II(1:100)
           %record %format A(%byte %integer I, J, K, L %or %c
                             %half %integer P, Q)
                             ! Implementation-dependent type
           %record(A)%name X
            :
           X == RECORD(ADDR(II(J)))
            :
     Now, for example:
        X_I is a reference to the leftmost byte of II(J)
        X_P is a reference to the leftmost half-word of II(J)
  {EMAS IMP80: RECORD returns a reference to a record whose format is
  unspecified but "very large".  This is acceptable when assigning a
  reference to a reference variable, as described above, since in EMAS
  IMP80 the format of the reference on the right-hand side of such an
  assignment statement does not have to match that of the reference
  variable on the left-hand side.  However, a record variable cannot be
  assigned by a statement of the form
           RECVAR = RECORD(ADDRESS)
  since the sizes of the records on each side are not equal.  On the
  other hand, the form
           RECVAR <- RECORD(ADDRESS)
  is permitted; as many bytes as RECVAR can hold (determined by its
  format) will be transferred.}
  {IMP77: the standard map RECORD is of the form
           %record(*)%map RECORD(%integer AD)
  i.e. the reference returned by RECORD may be to a record of any
  format; the actual format is determined by the context.  Thus RECORD
  can be used to assign to a complete record.
  Example:
           %record %format AF(%integer %array X(1:20),
                              %string(10) TITLE)
           %record(AF) A
           %integer AD
            :
            :
           A = RECORD(AD) {The record format used is that of A, i.e. AF}
            :
  Note that statements of the form
           RECORD(I) = RECORD(J)
  are not allowed, since no format is indicated from the context.}
  {EMAS IMP80: programmer-written record maps are not available.}
Standard Procedures
The standard procedures are those provided automatically, i.e. no
specification statements are required for them.  The standard procedures
are listed below, with an indication of where their descriptions may be
found: either a section number of this manual, or "I-S" meaning that the
procedure's precise effect is implementation-specific.  The relevant
documents for various implementations of IMP80 are given in Appendix B.
Notes
* At the time of writing, existing implementations of IMP80 do not have
  a complete common set of standard procedures.  The first list below
  includes procedures common to all the implementations.  The other
  lists relate to specific implementations.
* A standard procedure will not be provided in an implementation if it
  is a function or map, or has a parameter, of a type not provided in
  the implementation.
* Implementations of IMP80 may provide procedures other than those
  listed here which do not require specification statements.  In
  addition, various libraries of external procedures may be provided.
  These are not included here; consult the relevant documentation for
  details.
* Two named constants are predefined:
     %constant %integer NL = {value for newline char.  ISO value is} 10
     %constant %long %real PI = 3.141592653589793
* {EMAS IMP80: a standard procedure can be intrinsic.  An intrinsic
  procedure is one which is compiled as part of the program (for reasons
  of efficiency) rather than being separately compiled.  Intrinsic
  procedures cannot be passed as parameters.  The standard procedures
  which are intrinsic are noted in Appendix B.}
Standard Procedures
     Type                 Name and parameter list             Reference
  %integer %function      ADDR(%name A)                          6
  %byte %integer %map     BYTE INTEGER(%integer I)               6
  %integer %map           CHAR NO(%string(*)%name S, %integer I) 2
  %long %real %function   FLOAT(%integer I)                      2
  %long %real %function   FRAC PT(%long %real A)                 2
  %half %integer %map     HALF INTEGER(%integer I)               6
  %integer %function      IMOD(%integer I)                       2
  %integer %function      INT(%long %real A)                     2
  %integer %function      INT PT(%long %real A)                  2
  %integer %map           INTEGER(%integer I)                    6
  %byte %integer %map     LENGTH(%string(*)%name S)              2
  %long %integer %map     LONG INTEGER(%integer I)               6
  %long %long %real %map  LONG LONG REAL(%integer I)             6
  %long %real %map        LONG REAL(%integer I)                  6
  %long %real %function   MOD(%long %real A)                     2
  %routine                NEW LINE                               5
  %routine                NEW LINES(%integer I)                  5
  %integer %function      NEXT SYMBOL                            5
  %routine                PRINT(%long %real A, %integer I, J)    5
  %routine                PRINT FL(%long %real A, %integer I)    5
  %routine                PRINT STRING(%string(*) S)             5
  %routine                PRINT SYMBOL(%integer I)               5
  %routine                READ(%name A)                          5
  %routine                READ SYMBOL(%name I)                   5
  %real %map              REAL(%integer I)                       6
  %record %map            RECORD(%integer I)                     6
  %routine                SELECT INPUT(%integer I)               5
  %routine                SELECT OUTPUT(%integer I)              5
  %short %integer %map    SHORT INTEGER(%integer I)              6
  %integer %function      SIZE OF(%name A)                       2
  %routine                SKIP SYMBOL                            5
  %routine                SPACE                                  5
  %routine                SPACES(%integer I)                     5
  %string(*)%map          STRING(%integer I)                     6
  %string(*)%function     SUBSTRING(%string(*)%name S,
                                    %integer I, J)               2
  %string(*)%function     TO STRING(%integer I)                  2
  %routine                WRITE(%integer I, J)                   5
EMAS IMP80-Specific Procedures
     Type                 Name and parameter list             Reference
  %long %real %function   ARC COS(%long %real A)               I-S
  %long %real %function   ARC SIN(%long %real A)               I-S
  %long %real %function   ARC TAN(%long %real A, B)            I-S
  %array %map             ARRAY(%integer I, %array %format A)    6
  %routine                CLOSE STREAM(%integer I)               5
  %long %real %function   COS(%long %real A)                   I-S
  %long %real %function   COT(%long %real A)                   I-S
  %long %real %function   EXP(%long %real A)                   I-S
  %integer %function      EVENT INF(%integer I)                  3
  %integer %function      EVENT LINE(%integer I)                 3
  %long %integer %functionLENGTHEN I(%integer I)               I-S
  %long %long %real %function LENGTHEN R(%long %real R)        I-S
  %long %integer %functionLINT(%long %long %real I)            I-S
  %long %integer %functionLINT PT(%long %long %real I)         I-S
  %long %real %function   LOG(%long %real A)                   I-S
  %routine                NEW PAGE                               5
  %integer %function      NEXT CH                                5
  %string(*)%function     NEXT ITEM                              5
  %routine                PRINT CH(%integer I)                   5
  %long %real %function   RADIUS(%long %real A, B)             I-S
  %routine                READ CH(%name I)                       5
  %routine                READ ITEM(%string(*)%name S)           5
  %routine                READ STRING(%string(*)%name S)         5
  %integer %function      SHORTEN I(%long %integer I)          I-S
  %long %real %function   SHORTEN R(%long %long %real R)       I-S
  %long %real %function   SIN(%long %real A)                   I-S
  %long %real %function   SQRT(%long %real A)                  I-S
  %long %real %function   TAN(%long %real A)                   I-S
IMP77-Specific Procedures
     Type                 Name and parameter list             Reference
  %routine                CLOSE INPUT                            5
  %routine                CLOSE OUTPUT                           5
  %record %format         EVENT FM(%integer EVENT, SUB, EXTRA)   3
  %record(EVENT FM)%map   EVENT                                  3
  %integer %function      REM(%integer A, B)                   I-S
  %routine                RESET INPUT                            5
  %routine                RESET OUTPUT                           5
  %integer %function      TYPE OF(%name A)                     I-S
Examples (to follow)
(To follow.)
APPENDIX A: IMP80 Syntax
<letter>      ::=  A | B | C | D | E | F | G | H | I | J | K | L | M |
                   N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
                   a | b | c | d | e | f | g | h | i | j | k | l | m |
                   n | o | p | q | r | s | t | u | v | w | x | y | z
<digit>       ::=  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
<newline>     ::=  {newline character}
<bar>         ::=  {| character}
<character>   ::=  ! | " | # | $ | % | & | ' | ( | ) | * | + | , | - |
                   . | / | : | < | = | > | ? | @ | [ | ] | \ | ^ | _ |
                   ` | ~ | ; | <letter> | <digit> | <bar>
<text>        ::=  [<character>]...
<comment>     ::=  %comment <text> <newline> | ! <text> <newline>
<integer>     ::=  <digit> [<digit>]...
<letdig>      ::=  <letter> | <digit>
<name>        ::=  <letter> [<letdig>]...
<namelist>    ::=  <name> [, <name>]...
<int const>   ::=  [<plus>] <integer>
<frac>        ::=  . [<integer>]
<dec const>   ::=  [<int const>] [<frac>] [@ <int const>]
<base const>  ::=  <int const> _ <letdig>... | <letter> ' <letdig>... '
<charnl>      ::=  <character> | <newline>
<char const>  ::=  ' <charnl> ' | M' <charnl>... '
<str const>   ::=  " [<charnl>]... "
<const>       ::=  <int const> | <dec const> | <base const> |
                   <char const> | <str const>
<unop>        ::=  + | - | \ | ~
<assop>       ::=  = | == | <- | ->
<plus>        ::=  + | -
<comp1>       ::=  <comp2> | -> | == | \== | ##
<comp2>       ::=  = | # |  \= | <> | > | >= | < | <=
<op>          ::=  * | / | // | + | - | ** | **** | \ | \\ | >> | << |
                   ! | !! | & | .
<app>         ::=  ( <expr> [, <expr>]... )
<recel>       ::=  _ <name> [<app>]
<operand>     ::=  <name> [<app>] [<recel>]... | <const> | ( <expr> )
<expr>        ::=  [<unop>] <operand> [<op> <operand>]...
<count>       ::=  ( <expr> ) | (*)
<exprcount>   ::=  <expr> [<count>]
<constlist>   ::=  = <exprcount> [, <exprcount>]...
<decln>       ::=  [[%array] %name] <namelist> |
                   %array [%format] <adecln>
<adecln>      ::=  <namelist> <bpair> [, <namelist> <bpair>]...
<rfelmnt>     ::=  [[%array] %name] <namelist> | %array <adecln>
<rfdeclistor> ::=  ( <rfdeclist> [%or <rfdeclist>]... )
<rfdeclist>   ::=  <rfdec> [, <rfdec>]...
<rfdec>       ::=  <xtype> <rfelmnt> | <rfdeclistor>
<rfref>       ::=  ( <name> ) | <rfdeclistor>
<btype>       ::=  %integer | %real | %long %real
<type>        ::=  %integer | %real | %long <btype> | %byte [%integer] |
                   %short [%integer] | %half [%integer] | %string<count>
<xtype>       ::=  <type> | %record <rfref>
<oname>       ::=  <name> [%alias " <text> "]
<onamelist>   ::=  <oname> [, <oname>]...
<ownlist>     ::=  <onamelist> [= <expr>]
<owndec>      ::=  [[%array] %name] [%spec] <ownlist> [, <ownlist>]... |
                   %array [%format] [%spec] <oname> <bpair>[<constlist>]
<fm>          ::=  %fn | %function | %map
<rt>          ::=  %routine | <type> <fm>
<fpdel>       ::=  <xtype> [[%array] %name] <namelist> |
                   <rt> [%name] <namelist> [<fpp>] |
                   %name <namelist>
<fpp>         ::=  ( <fpdel> [, <fpdel>]... )
<range>       ::=  <expr> : <expr>
<bpair>       ::=  ( <range> [, <range>]... )
<xown>        ::=  %own | %external | %constant | %const
<%sed>        ::=  %system | %external | %dynamic
<endlist>     ::=  %of %program | %of %file | %of %list
<restofcond>  ::=  [%and <sc>]... | [%or <sc>]...
<sc>          ::=  <expr> <comp1> <expr> [<comp2> <expr>] |
                   ( <sc> <restofcond> ) |
                   %not <sc>
<restofcond>  ::=  [%and <sc>]... | [%or <sc>]...
<%wf>         ::=  %while <sc> <restofcond> |
                   %for <name> = <expr> , <expr> , <expr>
<%iu>         ::=  %if | %unless
<ui>          ::=  <name> [<app>] [<recel>]...
                       [<assop> <expr>] [%and <ui>] |
                   -> <name> [<app>] |
                   %return |
                   %result <assop> <expr> |
                   %monitor [%and <ui>] |
                   %stop |
                   %signal [%event] <const> [, <expr>] |
                   %exit |
                   %continue
<ci>          ::=  <%iu> <sc> <restofcond> <restofiu>
<restofiu>    ::=  [%then] %start | %then <ui> [<else>]
<else>        ::=  %else %start |
                   %else <ci> |
                   %else <ui>
<restofss>    ::=  <%iu> <sc> <restofcond> |
                   %until <sc> <restofcond> |
                   <%wf>
<s>           ::=  ; | <newline>
<program>     ::=  <ss>...
<ss>          ::=  %begin <s> |
                   <xtype> <decln> <s> |
                   %record %format <name> <rfdeclistor> <s> |
                   [<%sed>] <rt> [%spec] <name> [<fpp>] <s> |
                   <xown> <xtype> <owndec> <s> |
                   %include " <text> " |
                   %switch <namelist> <bpair>
                       [, <namelist> <spair>]... <s> |
                   %on [%event] [, <integer>]... %start <s> |
                   <ui> [<restofss>] <s> |
                   <ci> <s> |
                   %finish [<else>] <s> |
                   [<%wf>] %cycle <s> |
                   %repeat [%until <sc> <restofcond>] <s> |
                   <name> [<app>] : |
                   <name> (*) : |
                   %list <s> |
                   %end [<endlist>] <s> |
                   <comment> |
                   <s>
Notes
* In the above syntax, items in italics are non-terminals, '|' separates
  alternatives, '::=' means 'is defined as', items enclosed in [..]
  brackets are optional, and items followed by '...' can be repeated one
  or more times.  All other characters stand for themselves, except that
  {...} indicates a "descriptive" definition, involving a newline
  character or a character used in the statement of the syntax itself.
* The case of letters is ignored in IMP80 programs except within
  character constants.
* Space characters are only significant within character constants or
  following a keyword (%real, %if, %unless, %end, %not, etc.).
* <type> definition: not all the types may be available in some
  implementations.
* {EMAS IMP80: <type> definition: <count> replaced by [<count>]}
* {IMP77: <op> definition: ** | **** replaced by ^ | ^^ . }
* Text preceded by '{', terminated by '}' and appearing between
  syntactic elements of a program is treated as a comment.  Such a
  comment is also terminated by a newline character.
* IMP80 statements can be continued on the next line with %c.  The %c is
  not required if the break comes immediately after a comma.
  {IMP77: comment statements cannot be continued.}
APPENDIX B: Implementation Notes
This section will be expanded shortly.
EMAS IMP80
Compile-time errors
  The IMP compiler can generate any of the following messages while
  compiling a program.  The following points should be noted:
   * The symbols '#' and '##' appearing in any of the messages below are
     replaced by appropriate integer values when the message is output
     by the compiler.
   * The symbols '&' and '&&' appearing in any of the messages below are
     replaced by appropriate names (of program variables, routines,
     switches, etc.) when the message is output by the compiler.
   * Messages numbered 1-100 relate to standard compile-time errors.
     Messages numbered 101-200 relate to various compile-time limits
     (compiler table sizes, etc.) being exceeded.  Messages with numbers
     greater than 200 are warnings - they do not in themselves indicate
     an error in the program being compiled.
        1  %repeat is not required
        2  Label & has already been set in this block
        4  & is not a Switch name at current textual level
        5  Switch name & in expression or assignment
        6  Switch label &(#) set a second time
        7  Name & has already been declared
        8  Routine or fn & has more parameters than specified
        9  Parameter # of & differs in type from specification
       10  Routine or fn & has fewer parameters than specified
       11  Label & referenced at line # has not been set
       12  %cycle at line # has two control clauses
       13  %repeat for %cycle at line # is missing
       14  %end is not required
       15  # %ends are missing
       16  Name & has not been declared
       17  Name & does not require parameters or subscripts
       18  # too few parameters provided for &
       19  # too many parameters provided for &
       20  # too few subscripts provided for array &
       21  # too many subscripts provided for array &
       22  Actual parameter # of & conflicts with specification
       23  Routine name & in an expression
       24  Integer operator has real operands
       25  Real expression in integer context
       26  # is not a valid %event number
       27  & is not a routine name
       28  Routine or fn & has specification but no body
       29  %function name & not in expression
       30  %return outwith routine body
       31  %result outwith fn or map body
       34  Too many textual levels
       37  Array & has too many dimensions
       38  Array & has upper bound # less than lower bound
       39  Size of Array & is more than X'FFFFFF' bytes
       40  Declaration is not at head of block
       41  Constant cannot be evaluated at compile time
       42  # is an invalid repetition factor
       43  %constant name & not in expression
       44  Invalid constant initialising & after # items
       45  Array initialising items expected ## items given #
       46  Invalid %external, %extrinsic or variable %spec
       47  %else already given at line #
       48  %else invalid after %on %event
       49  Attempt to initialise %extrinsic or %format &
       50  Subscript of # is outwith the bounds of &
       51  %finish is not required
       52  %repeat instead of %finish for %start at line #
       53  %finish for %start at line # is missing
       54  %exit outwith %cycle %repeat body
       55  %continue outwith %cycle %repeat body
       56  %externalroutine & at wrong textual level
       57  Executable statement found at textual level zero
       58  Program among external routines
       59  %finish instead of %repeat for %cycle at line #
       61  Name & has already been used in this %format
       62  & is not a %record or %record %format name
       63  %record length is greater than # bytes
       64  Name & requires a subname in this context
       65  Subname & is not in the %record %format
       66  Expression assigned to record &
       67  Records && and & have different formats
       69  Subname && is attached to & which is not of type %record
       70  String declaration has invalid max length of #
       71  & is not a string variable
       72  Arithmetic operator in a string expression
       73  Arithmetic constant in a string-expression
       74  Resolution is not the correct format
       75  String expression contains a sub expression
       76  String variable & in arithmetic expression
       77  String constant in arithmetic expression
       78  String operator '.' in arithmetic expression
       80  Pointer variable & compared with expression
       81  Pointer variable & equivalenced to expression
       82  & is not a pointer name
       83  && and & are not equivalent in type
       86  Global pointer && equivalenced to local &
       87  %format name & use in expression
       90  Untyped name & used in expression
       91  %for control variable & not integer
       92  %for clause has zero step
       93  %for clause has noninteger number of traverses
       95  Name & not valid in assembler
       96  Operand # not valid in assembler
       97  Assembler construction not valid
      101  Source line has too many continuations
      102  Workfile of # Kbytes is too small
      103  Dictionary completely full
      104  Dictionary completely full
      105  Too many textual levels
      106  String constant too long
      107  Compiler tables are completely full
      108  Condition too complicated
      109  Compiler inconsistent
      201  Long integers are inefficient as subscripts
      202  Name & not used
      203  Label & not used
      204  Global %for control variable &
      205  Name & not addressable
      206  Semicolon in comment text
      255  Unexpected fault - consult ERCC Advisory Service
Miscellaneous notes
  <To follow.>
IMP77
  To follow
APPENDIX C: EMAS: IMP9 -> IMP80
Index
Type the entry you require (or the first part of it) followed by '?'.
The resulting screenful should contain the entry; if it doesn't then
the entry isn't in the index.
Examples:
           record?
           cons?
You can access the index from anywhere in the file by prefixing the above
with 'index,'.  Thus
           index,byte?
!!                            2.5/3
&                             2.5/3
(*)       2.2.5/4, 3.2.4/3, 4.2.3/3
**                            2.4.1
****                          2.4.1
+                             2.4.1
-                             2.4.1
->                              2.6
.                             2.6/2
/                             2.4.1
<-             2, 2.4.1, 2.5/2, 2.6
<<                            2.5/3
=                   2.4.1, 2.5, 2.6
==                            2.2.4
>>                            2.5/3
ADDR                          6.1.2
Advisory Service                T/4
Algol 60                        T/5
   drawbacks                    T/6
%alias                        3.3.3
%and                          4.1/5
ARC COS                         7.2
ARC SIN                         7.2
ARC TAN                         7.2
arithmetic expression         2.4.2
arithmetic operator           2.4.1
   precedence               2.4.1/3
arithmetic variable           2.2.1
array                       2.2.1/3
   dynamic bound            2.2.1/5
ARRAY (mapping fn)          6.1.2.1
array type                    2.1.4
Atlas Autocode                  T/6
base constant                 2.3.2
binary i/o                      5.2
block structure            3.1, T/6
BNF                             T/3
Burroughs Algol                T/10
BYTE INTEGER                6.1.2/2
byte integer                2.1.1/2
%c                            1.2.1
channel                       4.4.3
character
   constant                   2.3.3
   input                      5.1.1
   I/O                          5.1
   non-graphic                1.1/6
   output                     5.1.3
   set                          1.1
CHAR NO                       2.6/8
CLOSE INPUT                 5.1.5/2
CLOSE OUTPUT                5.1.5/2
CLOSE STREAM                  5.1.5
comma                         1.2.1
comment                     1.2.3.1
comparator                    4.1/2
compound instruction          4.2/2
condition                       4.1
   %and                       4.1/5
   comparator                 4.1/2
   compound                   4.1/4
   double-sided               4.1/3
   %not                       4.1/4
   %or                        4.1/5
   order of testing           4.1/7
   simple                       4.1
   string resolution          4.1/4
conditional repetition        4.4.1
%constant                     2.2.5
   array                    2.2.5/4
   record                   2.2.5/7
   ref var                  2.2.5/7
   string                   2.2.5/6
constant               1.2.2.2, 2.3
   base              1.2.2.2, 2.3.2
   character         1.2.2.2, 2.3.3
   decimal           1.2.2.2, 2.3.1
   multi-character          1.2.2.2
   named             1.2.2.2, 2.3.5
   real                     1.2.2.2
   string            1.2.2.2, 2.3.4
contents                        T/1
continuation                  1.2.1
%continue                     4.4.3
% convention                1.2.2.4
COS                             7.2
COT                             7.2
cycle                           4.4
   endless                      4.4
   %for                       4.4.2
   %repeat until            4.4.2/5
   simple form                4.4.2
data type                       2.1
decimal constant              2.3.1
DEL                           1.1/6
Edinburgh                       T/7
   environment                 T/13
%else                       4.2.1/2
EM                            1.1/6
EMAS                      T/8, T/12
EMAS IMP80                      T/3
endless cycle                   4.4
%end %of %list              1.2.3.3
E OF
   EMAS IMP80-specific          7.2
error messages               10.1.1
event                         3.1.1
EVENT INF                   3.1.1/5
EVENT LINE                  3.1.1/6
event list                  3.1.1/2
executable statement              4
%exit                         4.4.3
EXP                             7.2
expression
   arithmetic                 2.4.2
%external                2.2.5, 3.3
external
   array                    2.2.5/4
   file                       3.3.1
      example                 3.3.2
   linkage                      3.3
   procedure                  3.2/8
   record                   2.2.5/7
   ref var                  2.2.5/7
   string                   2.2.5/6
FF                            1.1/7
FLOAT                     2.4.2.2/3
%for cycle                    4.4.1
FRAC PT                   2.4.2.2/3
function                      3.3.3
   call                     3.3.3/2
   intrinsic                    7/2
   %result statement          3.3.3
   side effect              3.3.3/2
global                        3.1/5
HALF INTEGER                6.1.2/2
half integer                2.1.1/3
IBM 370                       2.2.1
ICL 2900                      2.2.1
identifier                  1.2.2.1
%if                           4.2.1
IMOD                      2.4.2.1/3
IMP77                           T/3
IMP80
   block-structured               1
   comparison with AA          T/11
   comparison with Algol60        1
   element                        1
   high-level                     1
   history                      T/5
   implementation               T/2
   machine code                T/10
   object code                  T/9
   philosophy                   T/9
   readability                 T/10
   recent development          T/12
   run-time support             T/9
implementation
   compile-time errors       10.1.1
   EMAS IMP80                  10.1
IMP77-specific                  7.3
%include                    1.2.3.2
input                             5
instruction                     4.2
   conditional                4.2.1
INT            2.4.2.1/3, 2.4.2.1/4
INTEGER                     6.1.2/2
integer                     2.1.1/3
integer expression          2.4.2.1
INT PT                    2.4.2.1/3
intrinsic function              7/2
I/O                           4.4.3
   binary                       5.2
jump                          4.2.2
KDF9                            T/7
keyword                     1.2.2.4
keyword list              1.2.2.4/2
label                         4.2.2
LENGTH                        2.6/8
LENGTHEN I                      7.2
LENGTHEN R                      7.2
LINT                            7.2
LINT PT                         7.2
%list                       1.2.3.3
LOG                             7.2
logical operators               2.5
long integer                2.1.1/3
LONG LONG REAL              6.1.2/2
long long real              2.1.1/3
LONG REAL                   6.1.2/2
long real                   2.1.1/3
lower case                    1.1/5
Manchester University           T/5
   Mark 1                       T/5
   Mercury                      T/5
manual                          T/3
   conventions              1.2.2.4
   errors                       T/4
mapping function         3.2.4, 6.1
   example                    6.1.1
   %result statement          3.2.4
   standard                   6.1.2
MOD                       2.4.2.2/3
%monitor                      4.2.5
named constant                2.3.5
%name-type variable           2.2.4
NEW LINE                    5.1.3/3
NEW LINES                   5.1.3/4
NEW PAGE                    5.1.3/4
NEXT CH                     5.1.1/6
NEXT ITEM                   5.1.1/5
NEXT SYMBOL                 5.1.1/2
NL                   1.1/6, 2.2.5/2
%not                          4.1/4
numeric input                 5.1.2
numeric output                5.1.4
%on %event                    3.1.1
operator
   arithmetic                 2.4.1
%or                           4.1/5
%own                          2.2.5
   declaration              2.2.5/2
   initialisation           2.2.5/3
%own array                  2.2.5/4
%own array initialisation   2.2.5/5
%own record                 2.2.5/7
%own reference variable     2.2.5/7
%own string                 2.2.5/6
paging                          T/5
parameter                     3.2.1
   name                       3.2.1
   procedure                3.2.1/8
   string                   3.2.1/8
   summary                 3.2.1/10
   value                      3.2.1
PI                          2.2.5/2
PL360                          T/10
pointer variable              2.2.4
precision                     2.2.1
PRINT                       5.1.4/2
PRINT CH                    5.1.3/3
PRINT FL                    5.1.4/3
PRINT STRING                5.1.3/2
PRINT SYMBOL                  5.1.3
procedure                       3.2
   external                   3.2/8
   first line                 3.2/4
   global variable            3.2/6
   heading                    3.2/4
   parameter                  3.2.1
   recursive                  3.2/8
   %spec statement            3.2/5
   standard                   3.2/8
   user-written               3.2/8
program
   example                    1.2/3
   structure                    1.2
quotes (' and ")              1.1/4
RADIUS                          7.2
READ                          5.1.2
READ CH                     5.1.1/6
READ ITEM                   5.1.1/4
READ STRING                 5.1.1/3
READ SYMBOL                   5.1.1
REAL                        6.1.2/2
real                        2.1.1/3
real expression             2.4.2.2
record
   alternative format       2.2.3/3
   array                    2.2.3/8
   assigment to 0          2.2.3/10
   assignment operator      2.2.3/9
   explicit format          2.2.3/5
   list processing          2.2.4/7
   record name for format   2.2.3/5
   sub-field       2.2.3/2, 2.2.3/6
record format                 2.2.3
record format spec         2.2.3/11
RECORD (mapping fn)         6.1.2.2
record name                 2.2.4/5
record type                   2.1.3
record variable               2.2.3
recursive procedure           3.2/8
reference type                2.1.5
reference variable       2.2.4, 6.1
   assignment to              2.2.4
REM                             7.3
%repeat until               4.4.1/5
RESET INPUT                 5.1.5/2
RESET OUTPUT                5.1.5/2
%result=      3.2.4, 3.2.4/3, 3.3.3
   use                      3.2.4/2
Robertson, Peter                T/3
routine                       3.2.2
routine call                  3.2.2
running a program             1.2/2
scope                         3.1/5
SELECT INPUT                    5.1
SELECT OUTPUT                   5.1
SHORTEN I                     7.2/2
SHORTEN R                     7.2/2
SHORT INTEGER               6.1.2/2
short integer               2.1.1/2
%signal %event              3.1.1/4
SIN                           7.2/2
SKIP SYMBOL                 5.1.1/3
SPACE                       5.1.3/3
space char                    1.1/5
SPACES                      5.1.3/3
special symbol              1.2.2.3
   use                    1.2.2.3/2
%spec statement               3.2/5
SQRT                          7.2/2
stack                    3.1/6, T/6
stack pointer                 3.1/6
standard procedure         3.2/8, 7
   intrinsic                    7/2
   list of                      7.1
%start/%finish                  4.3
statement                       1.2
   component                  1.2.2
   continuation               1.2.1
statement type                1.2/5
Stephens, Peter                 T/3
%stop                         4.2.4
storage allocation            3.1/6
store mapping                     6
stream                        4.4.3
   closing                    5.1.5
STRING                      6.1.2/2
string
   comparing                2.2.2/4
   concatenation              2.6/3
   resolution                 2.6/3
string constant               2.3.4
%string %name               2.2.4/4
string operator                 2.6
string resolution             2.6/3
string type                   2.1.2
string variable               2.2.2
SUB                           1.1/6
SUBSTRING                     2.6/8
switch                        4.2.3
syntax                            9
   notes                        9.1
TAN                           7.2/2
%then                         4.2.1
TO STRING                     2.6/9
type                            2.1
   arithmetic                 2.1.1
   array                      2.1.4
   integer                    2.1.1
   real                       2.1.1
   record                     2.1.3
   reference                  2.1.5
   string                     2.1.2
TYPE OF                         7.3
user-written procedure        3.2/8
variable                        2.2
   arithmetic                 2.2.1
   pointer                    2.2.4
   record                     2.2.3
   reference                  2.2.4
   string                     2.2.2
%while cycle                  4.4.1
Whitfield, Prof. H.             T/7
WRITE                         5.1.4
\                      2.4.1, 2.5/3
\\                            2.4.1
^                             2.4.1
^^                            2.4.1
|...|                     2.4.2.2/3
~                             2.5/3