!TITLE Blocks and Procedures !KEY ! %start %finish is a list of integers in the range 1 to 14 inclusive, where each number refers to a specified class of error as follows: !PAGE 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} !PAGE {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 , where specifies the event required and 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. !PAGE %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. !PAGE 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 !PAGE 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 1ERROR EXIT %finish : : ERROR EXIT: .... !PAGE 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: ..... !PAGE {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. !> !> ! () %function () %map () where is one of the arithmetic types, or %string() or %record(), and () is optional. !PAGE {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. !PAGE 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 !PAGE 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. !PAGE 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. != 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 !PAGE 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. !PAGE 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: !PAGE Example: %begin %routine ONE(%routine PARAM(%integer X) ) : : %end %routine TWO(%integer P) : : %end %routine THREE(%real X) : : %end ONE(TWO) %end !PAGE 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. !> ! ! 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.} !PAGE 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. !PAGE 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. !PAGE 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.} !> ! 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.} !PAGE {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. !PAGE 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). !PAGE 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 : !PAGE {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. !> !> ! ! !<%alias !KEY 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 !> !>