declaration = simple-variable-declaration | array-declaration | switch-declaration | procedure-declaration | class-declaration | external-declaration
For external declarations, see chapter 6.
Declarations serve to define certain properties of the quantities used in the program, and to associate them with identifiers. A declaration of an identifier is valid within a certain region of the program called its "scope". Outside the scope the particular identifier may be used for other purposes.
Within its scope the identifier declaration may be either "visible" or "invisible". If visible an occurrence of the identifier references this declaration. A declaration is invisible (within its scope) either because the associated identifier has been redefined or because its visibility has been explicitly restricted (see 5.5.4) or because connection or remote access is necessary to make it visible.
Dynamically this implies that at the time of entry into a block instance all identifiers declared for the block assume the significance implied by the nature of the declarations given. If these identifiers are defined outside they are, for the time being, given a new significance. Identifiers which are not declared for the block, on the other hand, retain their old meaning.
Apart from labels (see 4.10), formal parameters of procedure and class declarations, possible identifiers introduced implicitly by external declarations, and identifiers declared in the program prefix, each identifier appearing in a program must be explicitly defined within the program.
No identifier may be declared either explicitly or implicitly more than once in any one block head.
More precise scope and visiblity rules are given in 5.6.
simple-variable-declaration = type type-list type-list = type-list-element { , type-list-element } type-list-element = identifier | constant-element
Type declarations serve to declare certain identifiers to represent simple variables of a given type (see 3.1.1 and 3.1.2).
For constant element, see 5.8.
Real type variables may assume positive and negative values including zero.
Integer type variables may assume positive and negative integral values including zero.
Boolean variables may assume the values true and false.
character variables may assume the implementation-defined character values of the internal character set (cf. 1.2).
array-declaration = [ type ] array array-segment { , array-segment } array-segment = array-identifier { , array-identifier } "(" bound-pair-list ")" array-identifier = identifier bound-pair-list = bound-pair { , bound-pair } bound-pair = arithmetic-expression : arithmetic-expression
An array declaration declares one or several identifiers to represent multi-dimensional arrays of subscripted variables and gives the dimensions of the arrays, the bounds of the subscripts, and the type of the variables.
The subscript bounds for any array are given in the first subscript brackets following the identifier of this array in the form of a bound pair list. Each bound pair gives the lower bound of a subscript followed by : followed by the upper bound. The bound pair list gives the bounds of all subscripts taken in order from left to right.
Note: An initial "-" in upper bound may follow : directly (cf. 1.3).
The dimension is given as the number of entries in the bound pair lists.
All arrays declared in one declaration are of the same quoted type. If no type declarator is given the type real is understood.
The expressions are evaluated in the same way as subscript expressions. This evaluation takes place once at each entrance into the block through the block head. The expressions cannot include any identifier that is declared, either explicitly or implicitly, in the same block head as the array in question.
An array has elements only when the values of all upper bounds are not smaller than those of the corresponding lower bounds. If any lower bound value is greater than the corresponding upper bound value, the array has no elements. An attempt to access an element of an empty array leads to a run-time error. The array may, however, be created at block entry and it may be passed as a parameter.
The value of an array identifier is the ordered set of values of the corresponding array of subscripted variables.
Examples
integer array a(2:20) ! 19 elements; real array q(-7:if c<0 then 2 else 1) ! 10 or 9 elements; array a,b,c(7:n,2:m), s(-2:10) ! any value of n or m legal;
switch-declaration = switch switch-identifier := switch-list switch-list = designational-expression { , designational-expression }
A switch declaration defines the set of values of the corresponding switch designators. These values are given one by one as the values of the designational expressions entered in the switch list. With each of these designational expressions there is associated an ordinal number (1,2, ...) obtained by counting the items in the list from left to right. The value of the switch designator corresponding to a given value of the subscript expression is the value of the designational expression in the switch list having this given value as its associated integer.
An expression in the switch list is evaluated every time the item of the list in which the expression occurs is referred to, using the current values of all variables involved. This evaluation takes place in the context of the switch declaration.
Examples
switch s := s1,s2,q(m), if v>-5 then s3 else s4 switch q := p1,w
procedure-declaration = [ type ] procedure procedure-heading ; procedure-body procedure-heading = procedure-identifier [ formal-parameter-part ; [ mode-part ] specification-part ] procedure-body = statement procedure-identifier = identifier
A procedure declaration serves to define the procedure associated with a procedure identifier. The principal constituent of a procedure declaration is a statement, the procedure body, which through the use of procedure statements and/or function designators may be activated within those parts of the program in which the procedure declaration is visible.
Associated with the body is a heading, which specifies certain identifiers occurring within the body to represent formal parameters. Formal parameters in the procedure body are, whenever the procedure is activated, assigned the values of or replaced by actual parameters. Identifiers in the procedure body which are not formal are either local or non-local to the body depending on whether they are declared within the body or not. Those of them which are non-local to the body may well be local to the block in the head of which the procedure declaration appears.
The procedure body always acts like a block, whether it has the form of one or not. Consequently the scope of any label labelling a statement within the body or the body itself can never extend beyond the procedure body.
In addition, a procedure with parameters in certain respects acts as if a fictitious block embraced the procedure body. The formal parameters then correspond to variables declared local to this fictitious block. Thus, if the identifier of a formal parameter is declared anew within the procedure body, it is thereby given a local significance and actual parameters which correspond to it are invisible throughout the scope of this inner local quantity.
No identifier may appear more than once in any one formal parameter list, nor may a formal parameter list contain the procedure identifier of the same procedure heading.
See also 4.6.
Examples:
procedure transpose(a,n); array a; integer n; begin real w; integer i,k; for i:=1 step 1 until n do for k:=1+i step 1 until n do begin w:=a(i,k); a(i,k):=a(k,i); a(k,i):=w end end transpose; integer procedure factorial(n); integer n; factorial:= if n=0 then 1 else n*factorial(n-1); procedure absmax(a,n,m,y,i,k); name i, k, y ; array a; integer n,m,i,k; real y; comment The absolute greatest element of the matrix a, of size n by m is transferred to y, and the subscripts of this element to i and k; begin integer p,q; y:=0; i:=k:=1; for p:=1 step 1 until n do for q:=1 step 1 until m do if abs(a(p,q))>y then begin y:=abs(a(p,q)); i:=p; k:=q end end absmax; procedure innerproduct(a,b,k,p,y); name p,y,a,b; integer k,p; real y,a,b; begin real s; integer pp; s:=0; for pp:=1 step 1 until k do begin p:= pp; s:=s+a*b; end; y:=s end innerproduct; text procedure mystrip(t); text t; mystrip:- if t.sub(t.length,1)=" " then mystrip(t.sub(1,t.length-1)) else t;
For a procedure declaration to define the value of a function designator, the type associated with the procedure identifier must be declared through the appearance of a type declarator as the very first symbol of the procedure declaration. This identifier is implicitly assigned an initial value (see 5.7). The identifier may, in addition, occur (one or more times) as a destination within the procedure body.
The (dynamically) last value so assigned is used to continue the evaluation of the expression in which the function designator occurs. Any visible occurrence of the procedure identifier within the body of the procedure other than as a destination in an assignment statement denotes (recursive) activation of the procedure.
If a goto-statement within the procedure, or within any other procedure activated by it, leads to an exit from the procedure, other than through its end, then the execution of all statements that have been started but not yet completed and which do not contain the label to which the goto-statement leads, is abandoned. The values of all variables that still have significance remain as they were immediately before execution of the goto-statement (cf. 7.3.5).
If a function designator is used as a procedure statement, then the resulting value is discarded, but such a statement may be used, if desired, for the purpose of invoking side effects.
formal-parameter-part = "(" formal-parameter { , formal-parameter } ")" formal-parameter = identifier specification-part = specifier identifier-list { ; specifier identifier-list } specifier = type [ array | procedure ] | label | switch
The procedure heading may include a specification part, giving information about the kinds and types of the formal parameters. In this part no formal parameter may occur more than once.
mode-part = name-part [ value-part ] | value-part [ name-part ] name-part = name identifier-list ; value-part = value identifier-list ; identifier-list = identifier { , identifier }
There are three modes of parameter transmission: "call by value", "call by reference" and "call by name".
The default transmission mode is call by value for value type parameters and call by reference for all other kinds of parameters.
The available transmission modes are shown in fig. 5.1 for the different kinds of parameters to procedures.
-------------------------------------------------------------- | | Transmission modes | | Parameter | - - - - - - - - - - - - - - - - - - -| | | by value | by reference | by name | |--------------------------------------------------------------| | value type | D | I | O | | object ref. type | I | D | O | | text | O | D | O | | value type array | O | D | O | | reference type array| I | D | O | | procedure | I | D | O | | type procedure | I | D | O | | label | I | D | O | | switch | I | D | O | -------------------------------------------------------------- D: default mode O: optional mode I: illegal fig. 5.1 Transmission modes for procedures
class-declaration = [ prefix ] main-part prefix = class-identifier main-part = class class-identifier [ formal-parameter-part ; [ value-part ] specification-part ] ; [ protection-part ; ] [ virtual-part ; ] class-body class-identifier = identifier class-body = statement | split-body split-body = initial-operations inner-part final-operations initial-operations = ( begin | block-head ; ) { statement ; } inner-part = { label : } inner final-operations = end | ; compound-tail
A class declaration serves to define the class associated with a class identifier. The class consists of "objects" each of which is a dynamic instance of the class body.
An object is generated as the result of evaluating an object generator, which is analogous to the evaluation (call) of a function designator, see 3.8.2 or 4.7.
A class body acts like a block whether it takes that form or not. A split body acts as a block in which the symbol inner represents a dummy statement.
For a given object the formal parameters, the quantities specified in the virtual part, and the quantities declared local to the class body are called the "attributes" of the object. A declaration or specification of an attribute is called an "attribute definition".
Specification (in the specification part) is necessary for each formal parameter. The parameters are treated as variables local to the class body. They are initialized according to the rules of parameter transmission, (see 5.5.5 below). The following specifiers are accepted:
, array, and array.
Note: Call by name is not available for parameters of class declarations.
Attributes defined in the virtual part are called "virtual quantities". They do not occur in the formal parameter list. The virtual quantities have some properties which resemble formal parameters called by name. However, for a given object the environment of the corresponding "actual parameters" is the object itself, rather than that of the generating call. See 5.5.3.
Identifier conflicts between formal parameters and other attributes defined in a class declaration are illegal.
The declaration of an array attribute may in a constituent subscript bound expression make reference to the formal parameters of the class declaration, but subscript bound expressions which refer to attributes other than the formal parameters of the class declaration (or its prefixes, see 5.5.2) are illegal.
A class declaration with the prefix "C" and the class identifier "D" defines a subclass D of the class C. An object belonging to the subclass consists of a "prefix part", which is itself an object of the class C, and a "main part" described by the main part of the class declaration. The two parts are "concatenated" to form one compound object. The class C may itself have a prefix.
The following restrictions must be observed in the use of prefixes:
Let C1, C2, ... , Cn be classes such that C1 has no prefix and Ck has the prefix Ck-1 (k = 2,3,...,n). Then C1, C2, ... , Ck-1 is called the "prefix sequence" of Ck (k = 2,3,...,n). The subscript k of Ck (k = 1,2,...,n) is called the "prefix level" of the class. Ci is said to "include" Cj if i <= j, and Ci is called a "subclass" of Cj if i > j (i, j = 1,2,...,n). The prefix level of a class D is said to be inner to that of a class C if D is a subclass of C, and "outer" to that of C if C is a subclass of D.
Example
Figure 5.2 depicts a class hierarchy consisting of five classes, A, B, C, D and E:
class A .....; A class B .....; B class C .....; B class D .....; A class E .....;
A capital letter denotes a class. The corresponding lower case letter represents the attributes of the main part of an object belonging to that class. In an implementation of the language, the object structures shown in fig. 5.3 indicate the allocation in memory of the values of those attributes which are simple variables.
A class A . - includes: A,B,C,D,E . . - outer to: B,C,D,E . . . . class B B E - includes: B,C,D . . - outer to: C,D . . - inner to: A . . . . C D Fig. 5.2 Objects of classes A, B, C, D and E respectively: .-------. .-------. .-------. .-------. .-------. . . . . . . . . . . . a . . a . . a . . a . . a . . . . . . . . . . . ------- .-------. .-------. .-------. .-------. . . . . . . . . . b . . b . . b . . e . . . . . . . . . ------- .-------. .-------. ------- . . . . . c . . d . . . . . ------- ------- Fig. 5.3
Let Cn be a class with the prefix sequence C1, C2, ... , Cn-1, and let X be an object belonging to Cn. Informally, the concatenation mechanism has the following consequences.
A compound object could be described formally by a "concatenated" class declaration. The process of concatenation is considered to take place prior to program execution. In order to give a precise description of that process, we need the following definition.
An occurrence of an identifier which is part of a given block is said to be an "uncommitted occurrence in that block", except if it is the attribute identifier of a remote identifier (see 5.5.6), or is part of an inner block in which it is given a local significance. In this context a "block" may be a class declaration not including its prefix and class identifier, or a procedure declaration not including its procedure identifier.
Note: An uncommitted identifier occurrence in a block may well have a local significance in that block.
The class declarations of a given class hierarchy are processed in an order of ascending prefix levels. A class declaration with a non-empty prefix is replaced by a concatenated class declaration obtained by first modifying the given one in two steps.
The concatenated class declaration is defined in terms of the given declaration, modified as above, and the concatenated declaration of the prefix class.
Example
class point(x,y); real x,y; begin ref (point) procedure plus(P); ref (point) P; plus:- new point(x+P.x, y+P.y); end point;
An object of the class point is a representation of a point in a cartesian plane. Its attributes are x, y and plus, where plus represents the operation of vector addition.
point class polar; begin real r,v; ref (polar) procedure plus(P); ref (point) P; plus :- new polar(x+P.y, y+P.y); r:= sqrt(x**2 + y**2); v:= arctan(x,y); end polar;
An object of the class polar is a "point" object with the additional attributes r, v and a redefined "plus" operation. The values of r and v are computed and assigned at the time of object generation.
virtual-part = virtual : virtual-spec ; { virtual-spec ; } virtual-spec = specifier identifier-list | procedure procedure-identifier procedure-specification
Virtual quantities serve a double purpose:
The following specifiers are accepted in a virtual part:
label, switch, procedure andprocedure.
A virtual procedure may optionally be specified with respect to its type, and the type, kind, and transmission mode of its parameters (if any) (see 6.3).
A virtual quantity of an object is either "unmatched" or is identified with a "matching" attribute, which is an attribute whose identifier coincides with that of the virtual quantity, declared at the prefix level of the virtual quantity or at an inner one. The matching attribute must be of the same kind as the virtual quantity.
A virtual procedure quantity that contains a procedure specification, can only be matched by a procedure of the same type, and with the same procedure heading as that of the procedure specification. Otherwise, the type of the matching quantity (at a given prefix level) must coincide with or be subordinate to (see 2.4) that of the virtual specification and that of any matching quantity declared at any outer prefix level.
At any given prefix level PL inner or equal to that of a virtual specification, and in the abscence of a procedure specification, the type of the virtual quantity is
It is a consequence of the concatenation mechanism that a virtual quantity of a given object can have at most one matching attribute. If matching declarations have been given at more than one prefix level of the class hierarchy, then the one is valid which is given at the innermost prefix level outer or equal to that of the main part of the object. The match is valid at all prefix levels of the object equal or inner to that of the virtual specification.
Example
The following class expresses a notion of "hashing", in which the "hash" algorithm itself is a "replaceable part".
class hashing (n); integer n; virtual: integer procedure hash; begin integer procedure hash(t); text t; begin integer i; while t.more do i:= i + rank(t.getchar); hash:= mod(i,n); end hash; text array table (0:n-1); integer entries; integer procedure lookup (t,old); name old; Boolean old; text t; begin integer i,istart; Boolean entered; i:= istart:= hash(t); while not entered do begin if table(i)==notext then begin table(i):- copy(t); entries:= entries + 1; entered:= true; old:=false end else if table(i) = t then old:= entered:= true else begin i:= i + 1; if i=n then i:=0; if i=istart then error("Table full.") end end; lookup:= i; end lookup; end hashing; hashing class ALGOL_hash; begin integer procedure hash(T); text T; begin integer i; character c; T.setpos(1); while T.more do begin c:= T.getchar; if c <> ' ' then i:= i + rank(c) end; hash:= mod(i,n); end hash; end ALGOL_hash;
protection-part = protection-specification { ; protection-specification } protection-specification = hidden identifier-list | protected identifier-list | hidden protected identifier-list | protected hidden identifier-list
The protection specification makes it possible to restrict the visibility of class attribute identifiers.
A class attribute, X, which is specified protected in class C is only visible:
In any other context the meaning of the identifier X is as if the attribute definition of X were absent.
Access to a protected attribute is, subject to the restriction above, legal by remote accessing.
A class attribute may be specified protected only at the prefix level of its definition. Note that a virtual attribute may only be specified protected in the class heading in which the virtual specification occurs.
Attributes of the classes Simset and Simulation are protected.
A visible class attribute, X, specified hidden in class C is not visible within subclasses of C or blocks prefixed by C or any subclass of C. In this context the meaning of the identifier X is as if the attribute definition of X were absent.
Only a protected attribute may be specified hidden. However, this specification may occur at a prefix level inner to the protected specification.
The effect of specifying an attribute hidden protected or protected hidden is identical to that of specifying it as both protected and hidden.
Conflicting or illegal hidden and/or protected specifications constitute a error.
Note: Specifying a virtual quantity hidden effectively disables further
matching at inner levels.
If in the prefix sequence there are several attributes with the same
identifier as that of a hidden specification, and these are previously
protected, but not hidden, the innermost accessible attribute is hidden.
There are two modes of parameter transmission available for classes: "call by value" and "call by reference".
The default transmission mode is call by value for value type parameters and call by reference for all other kinds of parameters.
The available transmission modes are shown in fig. 5.4 for parameters of class declarations.
-------------------------------------------------- | | Transmission modes | | Parameter | - - - - - - - - - - - - -| | | by value | by reference | |--------------------------------------------------| | value type | D | I | | object ref. type | I | D | | text | O | D | | value type array | O | D | | reference type array| I | D | -------------------------------------------------- D: default mode O: optional mode I: illegal fig. 5.4. Parameter transmission modes for classes
For further details on parameter transmission modes, see 4.6.
An attribute of an object is identified completely by the following items of information:
Item 2 is textually defined for any attribute identification. The prefix level of the class is called the "access level" of the attribute identification.
Consider an attribute identification whose item 2 is the class C. Its attribute identifier, item 3, is subjected to the same identifier substitutions as those which would be applied to an uncommitted occurrence of that identifier within the main part of C, at the time of concatenation. In that way, name conflicts between attributes declared at different prefix levels of an object are resolved by selecting the one defined at the innermost prefix level not inner to the access level of the attribute identification.
An uncommitted occurrence within a given object of the identifier of an attribute of the object is itself a complete attribute identification. In this case items 1 and 2 are implicitly defined as, respectively, the given object and the class associated with the prefix level of the identifier occurrence.
If such an identifier occurrence is located in the body of a procedure declaration (which is part of the object), then, for any dynamic instance of the procedure, the occurrence serves to identify an attribute of the given object, regardless of the context in which the procedure was invoked.
Remote accessing of attributes, i.e. access from outside the object, is either through the mechanism of "remote identifiers" ("dot notation") or through "connection".
A text variable is (itself) a compound structure in the sense that it has attributes accessible through the dot notation.
Any class that has no (textually given) prefix is by definition prefixed by a fictitious class whose only attribute is:
procedure detach; ... ; (see 7.3.1)
Thus every class object or instance of a prefixed block has this attribute.
This section contains the scope and visibility rules governing the identifiers introduced in the program.
An identifier is introduced either implicitly (through a definition in one of the system classes ENVIRONMENT and BASICIO, or as text attribute), or explicitly
The occurrence of a system-defined class identifier as a prefix (see 5.5.1(2)) within some block leads to insertion of implicit declarations both of the referenced class and of all classes in its prefix chain (if any) in the head of the local block. Within any block such declarations are inserted only once. These declarations are then considered special cases of either rule 1 or 4 above.
Use of such an identifier other than as a prefix refers to either such an inserted declaration, or to the definition within ENVIRONMENT or BASICIO.
The "local block" of an identifier definition is the textually closest embracing block (subblock, prefixed block, or procedure or class body), including the fictitious blocks surrounding the controlled statement of a for- statement, a procedure or class declaration, a connection block etc. The identifier (and its definition) is said to be local within this block.
A distinction is made between the "scope" and the "visibility" of an identifier definition and its associated identifier as follows. The scope of an identifier definition is that part of the program text in which it may have an effect. An identifier definition is said to be visible at (or from) a given point in the program text when an occurrence of the identifier at this point can refer to the quantity of the declaration in question.
The same identifier may be defined in several places in the program and may consequently be associated with different quantities. The scopes of such definitions of the same identifier may thus overlap, for instance in the case of an identifier redeclared in an inner block.
At a particular point in the program text where a given identifier is visible there can be at most one definition associated with that identifier, e.g. in the case of redeclaration as mentioned only one of the definitions is visible at any given point within the union of their scopes.
The exact meaning of these terms will be defined in the sequel for each kind of definition.
Identifier definitions can only be visible within their scope. The visibility of a particular definition may within its scope be restricted by
Redefinition of an identifier is not allowed at the head of its local block.
Note: This prohibits the occurrence of two definitions of the same identifier in the same block. A formal procedure parameter may, however, be redefined in the head of the procedure body belonging to its procedure declaration. Such a redeclaration effectively restricts the visibility of the formal parameter to its associated value part, name part and specification part. Thus the only effect of such a parameter is the possible side effects resulting from its evaluation.
In the detailed visibility rules given below the word "visible" means "visible but for possible effects of redefinitions or other restrictions as stated above".
Apart from the static rules given in the preceding sections, all occurrences of an identifier (except within its definition) have a dynamic aspect. Such an identifier refers not only to its static definition but also to some object generated from the declaration to which the definition is local. Thus, for example, the occurrence of a formal parameter identifier within a procedure or class declaration references some instantiated object of the procedure or class.
See also 2.4.1 and 3.8.1.
Any declared variable is initialized at the time of entry into the block to which the variable is local. The initial contents depend on the type of the variable:
real-type 0.0 integer-type 0 Boolean false character '!0!' object ref. type none text notext
constant-element = identifier "=" value-expression | identifier "=" text-expression
An identifier which is declared by means of a constant element has a fixed value. Any attempt to assign to or otherwise alter the value of such an identifier constitutes an error. The evaluation of the expression takes place in the same manner as the evaluation of the bounds of an array. Thus any variables referenced in this expression contribute their values at the time of their evaluation, and any subsequent change does not affect the constant.
If the identifier is of arithmetic type then the value expression must also be of arithmetic type. Type conversion may be invoked following the rules given for arithmetic assignment (see 4.1.1). Otherwise, strict type correspondence is required between the type of the declaration and the type of the expression.
The constant element is subject to the following exception from the normal rules governing the occurrence of expressions in declarations.
The constant element of a block head are evaluated from left to right.