ALGOL 60

William.Waite@Colorado.edu

This document is an Eli specification from which an analyzer for ALGOL 60 can be generated, or which can be used as one component of as complete ALGOL 60 compiler. Its structure mirrors that of the ALGOL 60 Revised Report, providing traceability and easing maintenance.

The specification was originally developed as a class project at the University of Colorado by J. Amundsen, B. D. Basham, S-C. Chiang, D. A. Ence, R. K. Hill, J. E. Kvamme, O. Lokkebo, H. Ma, R. S. Matthieu, M. T. Rupawalla and W. Wang. It was put into its present form while the author was a Visiting Fellow at Australian National University.

1 Introduction
1.1 Formalism for Syntactic Description
1.1.1 Phrase Structure
1.1.2 Basic Symbols and Comments
1.2 Formalism for Semantically-Equivalent Symbols
1.3 Formalism for Semantic Description
1.3.1 Binding Identifier Uses to their Definitions
1.3.2 Verifying Type Consistency
1.4 Formalism for Properties of Quantities
1.5 Formalism for Modules and Interfaces
1.5.1 User-Defined Modules
1.5.2 Library Modules
2 Basic Symbols, Identifiers, Numbers, and Strings
2.1 Letters
2.2
2.2.1 Digits
2.2.2 Logical Values
2.3 Delimiters
2.4 Identifiers
2.5 Numbers
2.5.1 Syntax
2.5.2 Examples
2.5.3 Semantics
2.5.4 Types
2.6 Strings
2.7 Quantities, Kinds and Scopes
2.8 Values and Types
3 Expressions
3.1 Variables
3.1.1 Syntax
3.1.2 Equivalences
3.1.3 Semantics
3.1.4 Subscripts
3.2 Function Designators
3.2.1 Syntax
3.2.2 Equivalences
3.2.3 Semantics
3.2.4 Standard Functions
3.2.5 Transfer Functions
3.3 Arithmetic Expressions
3.3.1 Syntax
3.3.2 Equivalences
3.3.3 Semantics
3.3.4 Operators and Types
3.4 Boolean Expressions
3.4.1 Syntax
3.4.2 Equivalences
3.4.3 Semantics
3.4.4 Types
3.4.5 The Operators
3.5 Designational Expressions
3.5.1 Syntax
3.5.2 Equivalences
3.5.3 Semantics
4 Statements
4.1 Compound Statements and Blocks
4.1.1 Syntax
4.1.2 Equivalences
4.1.3 Semantics
4.2 Assignment Statements
4.2.1 Syntax
4.2.2 Equivalences
4.2.3 Semantics
4.2.4 Types
4.3 Go To Statements
4.3.1 Syntax
4.3.2 Equivalences
4.3.3 Semantics
4.4 Dummy Statements
4.5 Conditional Statements
4.6 For Statements
4.6.1 Syntax
4.6.2 Equivalences
4.6.3 Semantics
4.6.4 The for List Elements
4.7 Procedure Statements
4.7.1 Syntax
4.7.2 Equivalences
4.7.3 Semantics
4.7.4 Actual-Formal Correspondence
4.7.5 Restrictions
5 Declarations
5.1 Type Declarations
5.1.1 Syntax
5.1.2 Equivalences
5.1.3 Semantics
5.2 Array Declarations
5.2.1 Syntax
5.2.2 Equivalences
5.2.3 Semantics
5.3 Switch Declarations
5.3.1 Syntax
5.3.2 Equivalences
5.3.3 Semantics
5.4 Procedure Declarations
5.4.1 Syntax
5.4.2 Equivalences
5.4.3 Semantics
5.4.4 Values of Function Designators
5.4.5 Specifications
5.4.6 Code as Procedure Body

1 Introduction

This document was generated by the Eli system from a specification module that provides a complete description of ALGOL 60. ALGOL 60 was defined in the classic paper ``Revised Report on the Algorithmic Language ALGOL 60'', which appeared (among other places) in the January, 1963 issue of Communications of the ACM (pages 1-17). We have structured the specification module to correspond as closely as possible to that paper, so that it is easy for the reader to verify that the specification module describes the same language as the paper.

From this specification module, the Eli system can generate an executable program that checks its input to verify conformance to the rules of ALGOL 60. This specification module can also be combined with other specification modules to define additional analysis of the input program and/or translation of the algorithm embodied in that program to another language.

This specification defines a hardware language that differs from the reference language of the Revised Report in some of the symbols used to represent objects. These differences will be pointed out in the appropriate sections of this document.

We have also restricted the language in two ways: unsigned integers cannot be used as labels, and type specifications must be given for all procedure parameters. The former restriction avoids a known ambiguity, and the latter is necessary for compile-time determination of the types of expressions. Both are common in ALGOL 60 implementations (Randell, B., Russell, L. J. ``ALGOL 60 Implementation'').

1.1 Formalism for Syntactic Description

We have used a more modern form of BNF notation in which the quoting conventions are reversed: The Revised Report uses angle brackets to quote nonterminal symbols, and leaves terminal symbols unquoted; we leave nonterminal symbols unquoted and use apostrophes to quote terminal symbols.

One consequence of this quoting strategy is that spaces are not allowed within nonterminal names. In order to preserve the understandability of the nonterminal names used in the Revised Report, we have adopted the convention of running the words together and capitalizing the first letter of every word. Thus we render the Revised Report's nonterminal <simple variable> as SimpleVariable.

Definition:

The null string of symbols[1]==
Empty ::= .
This macro is invoked in definition 2.
The Revised Report uses a combination of BNF and English to describe the complete syntax of the language. Because this is an executable specification, we follow standard compiler practice, using BNF to describe the phrase structure and using a combination of regular expressions and C code to describe the basic symbols and comments.

1.1.1 Phrase Structure

A type-con file provides the definition of the context-free grammar to be used by the parser to recognize the structure of a program.
algol60.con[2]==
The null string of symbols[1]
Logical Values[22]
Numbers[38]
Variables[48]
Function Designators[53]
Expressions[47]
Compound Statements and Blocks[84]
Assignment Statements[86]
Go To Statements[90]
Dummy Statements[93]
Conditional Statements[94]
For Statements[95]
Procedure Statements[97]
Declarations[103]
This macro is attached to a product file.

1.1.2 Basic Symbols and Comments

The definition of a basic symbol or comment consists of a regular expression, an auxiliary scanner, and a token processor. Either the auxiliary scanner or the token processor, or both, may be omitted.

In a definition, the regular expression defines a sequence of characters that identifies the particular basic symbol or comment. If this character sequence does not constitute the complete basic symbol or comment, then an auxiliary scanner that obtains the complete sequence is specified. Finally, if an internal representation of the basic symbol or comment is required, then a token processor that builds the desired internal representation is specified.

For example, consider an ALGOL 60 string. This basic symbol is identified by the opening string quote `. A single opening string quote does not constitute the complete string, so we specify an auxiliary scanner called Algol60String to obtain the complete sequence. An internal representation of the string is required, so we specify the token processor mkidn to build that representation.

Auxiliary scanners and token processors for common situations can be found in the Eli library, or they can be written in C or C++ and provided with the specification. This document includes two C-coded auxiliary scanners, Algol60Comment and Algol60String, and uses one, Ctext, from the Eli library. Both of the token processors, mkidn and mkstr, are taken from the Eli library.

All auxiliary scanners obey the following interface specification:

Define an Auxiliary Scanner[3](¶1)==
char *
#if defined(__cplusplus) || defined(__STDC__)
¶1(char *start, int length)
#else
¶1(start, length)
char *start; int length;
#endif
/* Standard interface for an auxiliary scanner
 *   On entry-
 *     start points to the first character of the scanned string
 *     length=length of the scanned string
 *   On exit-
 *     The function returns a pointer to the first character
 *       beyond the scanned string
 ***/
This macro is invoked in definitions 25 and 41.
A type-gla file provides the definitions of the basic symbols to be recognized by the lexical analyzer. These definitions specify not only the form of the basic symbol, but also the mechanism by which its internal representation (if any) will be constructed.
algol60.gla[4]==
Delimiters[23]
Unsigned Numbers[37]
Identifiers[28]
Strings[40]
Parameter Delimiter Letter String[54]
Code as Procedure Body[121]
This macro is attached to a product file.
A type-c file provides auxiliary routines to handle tasks that must be defined operationally.
algol60.c[5]==
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "err.h"
#include "gla.h"
#include "source.h"
#include "tabsize.h"

Comment Scanner[25]
String Scanner[41]
This macro is attached to a product file.

1.2 Formalism for Semantically-Equivalent Symbols

Many distinct symbols are used in the syntax of the Revised Report refer to semantically-identical constructs. We group such symbols into equivalence classes, and use a single symbol in the semantic description. This reduces the size of the semantic description, and emphasizes the uniformity of meaning among the equivalent symbols.
algol60.map[6]==
MAPSYM
Equivalences[49]
This macro is attached to a product file.
Our specifications of equivalence classes have replaced the examples of ALGOL 60 code that appear in the Revised Report. Where no equivalence classes are defined, we have inserted an empty section in order to keep the section numbering parallel to that of the Revised Report.

1.3 Formalism for Semantic Description

We use an attribute grammar to describe the semantics of ALGOL 60 via a series of tree computations. The description specifies only the relationships that must hold among these computations; Eli can deduce the order in which the computations should be carried out, and decide on how (or indeed whether) to store intermediate values.

Relationships among computations are embodied in attributes, which constitute pre- and post-conditions for computations. An attribute may have a value, and that value may be of any type. Types are often defined by external modules that export a set of appropriate operations for use in computation. The pre- and post-conditions may relate computations locally, or remotely along root-to-leaf or depth-first, left-to-right paths.

Eli's attribute grammar notation is LIDO. A LIDO specification consists of a set of symbol and rule computations, with additional definitions of the types of attributes. All computations must be expressed as function application. (Non-strict functions are available for dealing with conditional computation.) Computations may be associated with specific contexts in the tree (rule computations), or with every context in which a particular symbol appears (symbol computations).

algol60.lido[7]==
Internal representation of an identifier[29]

Subscripts[52]

Scope Attributes[45]
Scope Violation Rules[30]
Statement Scope Rules[9]

Establish operator indications for source symbols[67]
Identify an operator from its context[13]
Balance the operands of a conditional[14]

Semantics[44]

Verify type consistency of a left-part list[89]
The for List Elements[96]
Actual-Formal Correspondence[99]
Restrictions[100]
Specifications[118]
This macro is attached to a product file.

1.3.1 Binding Identifier Uses to their Definitions

An identifier may have different meanings in different parts of an ALGOL 60 program. We deal with this by using a definition table key to represent the meaning of an identifier internally. Definition table keys are unique handles providing access to the specific set of properties that characterize an entity.

Identifiers are freely chosen by the programmer and given meaning through specific language constructs. The general strategy is to bind the definition of an identifier and all of its uses to the same definition table key. When processing a construct that gives meaning to an identifier, properties characterizing that meaning are associated with the definition table key to which the definition is bound, and when processing a construct that uses an identifier those properties are accessed.

Eli provides modules for establishing these bindings. Each module implements a specific set of rules governing the binding process, and any number of copies of any of the available modules can be instantiated in a given specification.

Instantiate the ALGOL 60 consistent renaming module[8]==
$/Name/AlgScope.gnrc :inst
This macro is invoked in definition 18.
This module exports a set of computational roles defining the behavior of the relevant constructs:
Statement Scope Rules[9]==
SYMBOL Program INHERITS RootScope END;
This macro is invoked in definition 7.

1.3.2 Verifying Type Consistency

ALGOL 60 places a number of context conditions on expressions, and those conditions are defined formally in this document. For each operator, three steps are required: an operator indication must be established for the operator's source language symbol, one or more operators must be associated with the indication, and the signatures of those operators must be defined.

Operator indications are established for each operator's source language symbol by computations described in LIDO:

Set a monadic operator indication[10](¶2)==
RULE: Uniop ::= '¶1'
COMPUTE Uniop.indication=OilOp¶2;
END;
This macro is invoked in definitions 67 and 78.
Set a dyadic operator indication[11](¶2)==
RULE: Binop ::= '¶1'
COMPUTE Binop.indication=OilOp¶2;
END;
This macro is invoked in definitions 67, 69, 71, 78, and 80.
Signatures for the operators are described, and operators associated with indications, by descriptions written in OIL:
algol60.oil[12]==
Define operators and associate them with indications[59]
Define an operator to be applied when needed[73]
Define operators to establish types without values[92]
This macro is attached to a product file.
OIL describes a set of constraints that the operators and expressions must satisfy. Those constraints must be verified by tree computations that use operations exported by the OIL library:
Identify an operator from its context[13]==
ATTR type: tOilType;
ATTR indication, operator: tOilOp;

RULE: Expression ::= Uniop Expression
COMPUTE
  Expression[1].type=OilGetArgType(Uniop.operator,0);
  Uniop.operator=OilIdOp1(Uniop.indication,Expression[2].type);
END;

SYMBOL Uniop COMPUTE
  IF(NOT(OilIsValidOp(THIS.operator)),
    message(ERROR, "Illegal operation", 0, COORDREF));
END;

RULE: Expression ::= Expression Binop Expression
COMPUTE
  Expression[1].type=OilGetArgType(Binop.operator,0);
  Binop.operator=
    OilIdOp2(Binop.indication,Expression[2].type,Expression[3].type);
END;

SYMBOL Binop COMPUTE
  IF(NOT(OilIsValidOp(THIS.operator)),
    message(ERROR, "Illegal operation", 0, COORDREF));
END;
This macro is invoked in definition 7.
The constraint on a conditional expression is that the type of the result must not depend on the (run-time) outcome of the condition:
Balance the operands of a conditional[14]==
RULE: Expression ::= IfClause Expression 'else' Expression
COMPUTE
  Expression[1].type=
    OilBalance(
      OilTypeToSet(Expression[2].type),
      OilTypeToSet(Expression[3].type));
END;
This macro is invoked in definition 7.

1.4 Formalism for Properties of Quantities

Each quantity is represented internally by a definition table key, which is used to access the properties of that quantity. Properties are declared individually in a type-pdl file:
algol60.pdl[15]==
"algol60.h"
"keyarray.h"
Properties characterizing all quantities[43]
Properties characterizing arrays[110]
Properties characterizing procedures[119]
Properties characterizing formal parameters[120]
Properties of standard functions[60]
Arity of transfer functions[63]
This macro is attached to a product file.
The declaration of a property specifies the type of value that property can hold. All C basic types, plus the type DefTableKey (the type of a definition table key) can be used without further specification; any other types must be specified by some type-h file whose name is given in the type-pdl file.

1.5 Formalism for Modules and Interfaces

Computations that verify context conditions are often expressed in terms of abstract data types that are exported by modules either defined by the user or obtained from Eli's library.

1.5.1 User-Defined Modules

A type-h file is used to define the interfaces for user-defined modules:
algol60.h[16]==
#ifndef ALGOL60_H
#define ALGOL60_H

Kinds of quantities[42]

#endif
This macro is attached to a product file.
Because type-h files may be included by several different specifications, they must be protected against multiple inclusion. Eli's convention is to use a symbol consisting of the file name rendered in upper case, with periods replaced by underscores.

The definitions must be made available to the routines that implement the computations, by including them in a type-HEAD.phi file:

algol60.HEAD.phi[17]==
#include "algol60.h"
This macro is attached to a product file.

1.5.2 Library Modules

Library modules are instantiated by Eli requests stored in a type-specs file:
algol60.specs[18]==
Instantiate the ALGOL 60 consistent renaming module[8]
$/Prop/Unique.gnrc :inst
$/Tech/Strings.specs
$/pdl/keyarray.specs
$/Name/PreDefine.gnrc +referto=Identifier :inst
$/Name/PreDefId.gnrc +referto=(algol60.d) :inst
This macro is attached to a product file.
The predefinition module requires that the specifications of predefined identifiers be supplied in a file whose name is passed as the referto parameter of the module's instantiation. This file is defined above as the result of the Eli request ALGOL60.fw:fwGen/algol60.d:
algol60.d[19]==
Standard Functions[58]
Transfer Functions[61]
This macro is attached to a product file.
Eli attaches no particular significance to the d suffix. It was chosen specifically for this reason, to avoid any unintended processing by Eli.

2 Basic Symbols, Identifiers, Numbers, and Strings

The basic symbols of our implementation language are identifiers, denotations and delimiters rather than the characters making them up.

Basic symbols are recognized by a finite-state machine, and therefore the definitions of the basic symbols are stated as regular expressions rather than productions of a context-free grammar. This means that all recursive definitions given in the Revised Report must be replaced by the equivalent iterative formulations. We have kept the regular expression notation as close to the form of the context-free grammar as possible to ease verification, except that we have replaced alternations with a more compact representation when describing sets of characters: [a-d] means a|b|c|d and [^;] means ``any character other than semicolon'', for example.

The scanner that recognizes basic symbols attaches source text coordinates (the line number and column number of the first character) to each basic symbol. This requires that the scanner keep track of the coordinates as it is scanning. Coordinate tracking is handled automatically for most basic symbols, but must be provided explicitly for basic symbols that may contain horizontal tab or newline characters. These cases are noted below when they occur.

2.1 Letters

letter[20]==
[a-zA-Z]
This macro is invoked in definition 28.
Letters are used only as components of other basic symbols.

2.2

2.2.1 Digits

digit[21]==
[0-9]
This macro is invoked in definitions 28 and 31.
Digits are used only as components of other basic symbols.

2.2.2 Logical Values

Logical Values[22]==
LogicalValue ::= 'true' / 'false' .
This macro is invoked in definition 2.
The logical values are denoted by keywords of the language.

2.3 Delimiters

Most delimiters appear as quoted literals in the grammar, so their definitions need not be repeated here. The exceptions are those delimiters associated with comments: ;, begin and end.
Delimiters[23]==
Semi:   $;([\040\t\n]*comment[^;]*;)?      (coordAdjust)
begin:  $begin([\040\t\n]+comment[^;]*;)?  (coordAdjust)
This macro is defined in definitions 23 and 24.
This macro is invoked in definition 4.
(\040 must be used to represent a space within a regular expression because any white space terminates the expression.)

Both Semi and begin are easy to define with regular expressions because they are self-delimiting. The regular expression accepts horizontal tabs and newlines, and therefore the auxiliary scanner coordAdjust must be used to adjust the coordinates appropriately.

Comments following end are much harder to deal with because they can not contain certain strings. It would be possible to write a regular expression that matched strings ending in end or ; or else, and then use an auxiliary scanner to accept everything up to but not including that symbol. The problem is that the scanner would then find the longest such string, and we need the shortest such string.

Thus we need to use an auxiliary scanner to pick up the comment string following an end:

Delimiters[24]==
end:    $end    (Algol60Comment)
This macro is defined in definitions 23 and 24.
This macro is invoked in definition 4.
An auxiliary scanner is invoked after the associated regular expression has been accepted. In this case, Algol60Comment will be invoked after the normal scanner has accepted the symbol end.

The auxiliary scanner is called with a pointer to the first character matched by the regular expression and the length of the string matching the regular expression; it should return a pointer to the first character that does not belong to the basic symbol being recognized. Thus start should point to the e of end, length should be 3, and Algol60Comment should return a pointer to the next end, ; or else.

The regular expression will match the characters e, n and d in that order. Since the scanner finds the longest match, the character following the d cannot be a letter or a digit. If it were, the scanner would combine it with the first three characters in searching for an identifier. The variable p is set to point to the character following the d, so after fetching that character to c and incrementing p the stated loop invariant holds:

Comment Scanner[25]==
Define an Auxiliary Scanner[3] (`Algol60Comment')
{ char *p = start + length;
  char c = *p++;

  for (;;) {    /* Invariant:
                 *   c is neither a letter nor a digit &&
                 *   p points to the first unexamined character
                 * Bound: Number of characters left in the file
                 ***/

    if (c == ';') return p - 1;

    Adjust coordinates if necessary[26] (`')

    if (*p == 'e') {
        if (strncmp(p, "end", 3) == 0 && !isalnum(p[3]) ||
            strncmp(p, "else", 4) == 0 && !isalnum(p[4]))
          return p;
    }

    while (isalnum(c = *p++)) ;
  }
}
This macro is defined in definitions 25.
This macro is invoked in definition 5.
If c is ;, then the scan should be terminated and the position of the semicolon returned. Otherwise, any necessary adjustment of the coordinates to reflect a tab or newline character must be made.

The first unexamined character, pointed to by p, could be anything. If it is an e, then the routine checks for the presence of one of the two keywords that can follow the comment. Each of the specified character sequences must be followed by a character other than a letter or digit if it is to be recognized as a keyword. When either of those keywords is found, the routine returns the position of that keyword. (Thus terminating the comment with the character preceding the keyword.)

Finally, the routine re-establishes the invariant of the outer loop.

The generated scanner keeps track of the line and column number of each basic symbol, so that the processor can report errors at the appropriate position in the source program. Tab and newline characters require special action to maintain these values:

Adjust coordinates if necessary[26](¶1)==
if (c == '\t') StartLine -= TABSIZE(p - StartLine);
else if (c == '\n') {
  Refill the source buffer if necessary[27] (`¶1')
  LineNum++;
  StartLine = p - 1;
}
This macro is invoked in definitions 25 and 41.
Character position within a line is the difference between the current character index and the character index StartLine. A tab character occupies one character position, but it may represent a longer sequence, depending upon it's position in the line. This additional white space is taken into account by moving StartLine backwards.

The coordinate adjustment code is needed for processing strings (Section 2.6) as well. These two cases differ in that it is an error for the file to end within a string, but no error if the file ends within a comment. If an end-of-file is an error, code to report that error must be introduced. That code is introduced through the parameter to the coordinate adjustment description.

Source text is stored as a sequence of characters in memory, and the last character of that sequence is guaranteed to be a newline. If the character following a newline is the ASCII NUL character, then there are no more characters in the sequence; otherwise, this newline is not the last newline of the sequence. Thus when the scanner reaches a newline, it must check whether that newline is the last of the sequence in memory:

Refill the source buffer if necessary[27](¶1)==
if (*p == '\0') {
  size_t pSave = p - start, sSave = p - StartLine;

  refillBuf(start); TokenStart = start = TEXTSTART;
  p = start + pSave; StartLine = start + sSave;

  if (*p == '\0') {
    ¶1
    return p - 1;
  }
}
This macro is invoked in definition 26.
Recall that p points to the character following the newline. If it is the ASCII NUL character, then there are no more characters of the file stored in memory. By calling refillBuf with start as an argument, the scanner extends the character sequence that begins at the location pointed to by start. Because of the way storage is allocated by the source text input module, it may be necessary to move the sequence to another location. Thus TokenStart (the starting location of the basic symbol), start, and p must all be adjusted. Finally, a check is made to see whether the character sequence was, in fact, extended. If it was not, then end-of-file has been reached, the comment string has ended, and the scanner terminates.

2.4 Identifiers

Identifiers[28]==
Identifier:     $letter[20](letter[20]|digit[21])*      [mkidn]
This macro is invoked in definition 4.
This regular expression is equivalent to the definition given in the Revised Report.

Distinct identifiers must be distinguishable from one another. The token processor mkidn assigns a unique integer value to each distinct identifier. This integer value can also be used to access the string representing the identifier, and is stored as the value of the Sym attribute of symbols representing identifier occurrences:

Internal representation of an identifier[29]==
ATTR Sym: int;
This macro is invoked in definition 7.
The same identifier cannot be used to denote two different quantities except when these quantities have disjoint scopes as defined by the declarations of the program:
Scope Violation Rules[30]==
SYMBOL MultDefChk INHERITS Unique COMPUTE
  IF(NOT(THIS.Unique),
    message(
      ERROR,
      CatStrInd("Identifier is multiply defined: ", THIS.Sym),
      0,
      COORDREF));
END;
This macro is defined in definitions 30, 46, and 102.
This macro is invoked in definition 7.

2.5 Numbers

2.5.1 Syntax

unsigned integer[31]==
digit[21]+
This macro is invoked in definitions 32, 33, 35, and 37.
This regular expression is equivalent to the definition given in the Revised Report.
integer[32]==
(unsigned integer[31]|\+unsigned integer[31]|-unsigned integer[31])
This macro is invoked in definition 34.
The + must be escaped because it is a regular expression operator.
decimal fraction[33]==
\.unsigned integer[31]
This macro is invoked in definition 35.
The . must be escaped because it is a regular expression operator.

Following standard practice, we represent the subscript 10 of the reference language by the letter E:

exponent part[34]==
(E|e)integer[32]
This macro is invoked in definitions 36 and 37.
By requiring a decimal number to contain a decimal fraction, we obtain distinct representations for integer and real constants:
decimal number[35]==
(decimal fraction[33]|unsigned integer[31]decimal fraction[33])
This macro is invoked in definition 36.
We cannot allow an unsigned number consisting of only an exponent part, because there would be no way for the scanner to distinguish it from an identifier (this is a consequence of our representing the subscript 10 of the reference language by the letter E):
unsigned number[36]==
(decimal number[35]|decimal number[35]exponent part[34])
This macro is invoked in definition 37.
We express the distinction between integer and real constants by introducing the new basic symbol UnsignedReal, which does not appear in the Revised Report:
Unsigned Numbers[37]==
UnsignedInteger: $unsigned integer[31]  [mkidn]
UnsignedReal:    $unsigned number[36]|unsigned integer[31]exponent part[34] [mkidn]
This macro is invoked in definition 4.
The token processor mkidn assigns a unique integer value to each distinct numeric denotation. (Note that distinct denotations may represent the same numeric value: 001 has the same value as 1 and 1.23E1 has the same value as 12.3.) This integer value can be used to access the denoting string.

An unsigned integer followed by an exponent part is legal according to the Revised Report. Because of the need to exclude unsigned integer from the definition of decimal number, however, this case has to be treated specially.

Numbers[38]==
UnsignedNumber: UnsignedInteger / UnsignedReal .
This macro is invoked in definition 2.
The symbol number does not appear elsewhere in the Revised Report, so it is not defined here.

2.5.2 Examples

2.5.3 Semantics

2.5.4 Types

Integers are of type integer. All other numbers are of type real.
Denotation types[39]==
RULE: Expression ::= UnsignedInteger
COMPUTE
  Expression.type=OilTypeIntegerKey;
END;

RULE: Expression ::= UnsignedReal
COMPUTE
  Expression.type=OilTypeRealKey;
END;
This macro is invoked in definition 66.

2.6 Strings

The Revised Report's definition of a string allows arbitrary nesting of character sequences delimited by ` and '. Such a structure cannot be described by a finite-state machine, and therefore an auxiliary scanner must be used:
Strings[40]==
String:         $`    (Algol60String)   [mkidn]
This macro is invoked in definition 4.
Algol60String will be invoked after the normal scanner has accepted the character `.
String Scanner[41]==
Define an Auxiliary Scanner[3] (`Algol60String')
{ char *p = start + length;
  int counter = 1;

                /* Invariant:
                 *   counter = number of unmatched open quotes
                 *   p points to the first unexamined character
                 * Bound: Number of characters left in the file
                 ***/
  while (counter) {
    char c = *p++;

    Adjust coordinates if necessary[26] (`message(ERROR,"End-of-file in string",0,&curpos);')

    if (c == '\'') counter--; else if (c == '`') counter++;
  }

  return p;
}
This macro is invoked in definition 5.

2.7 Quantities, Kinds and Scopes

The following kinds of quantities are distinguished: simple variables, arrays, labels, switches, and procedures.
Kinds of quantities[42]==
typedef enum {
  UndefinedIdn,
  VariableIdn,
  ArrayIdn,
  LabelIdn,
  SwitchIdn,
  ProcedureIdn
} KindOfQuantity;
This macro is invoked in definition 16.
UndefinedIdn is used to distinguish identifiers that are used but not declared. Every declared quantity is represented internally by a definition table key under which the kind is stored as a property:
Properties characterizing all quantities[43]==
Kind: KindOfQuantity;
This macro is defined in definitions 43 and 51.
This macro is invoked in definition 15.
The kind of quantity may also be stored as an attribute of certain nodes, and a dependence must be used to guarantee that all quantities have their Kind properties set before any are queried:
Semantics[44]==
ATTR kind: KindOfQuantity;

SYMBOL Program COMPUTE
  SYNT.GotAllProperties=
    CONSTITUENTS (
      VarIdDef.GotProperties,ArraySegment.GotProperties,
      ProcedureDeclaration.GotProperties,Label.GotProperties);
END;
This macro is defined in definitions 44, 50, 57, 66, 77, 83, 85, 87, 91, 98, 106, 109, 112, and 117.
This macro is invoked in definition 7.
The scope of a quantity is the set of statements and expressions in which the declaration of the identifier associated with that quantity is valid. A scope is represented internally by an environment, which is stored as the Env attribute of symbols representing certain sets of statements and expressions. A quantity is represented internally by a definition table key, which is stored as the Key attribute of symbols representing occurrences of identifiers:
Scope Attributes[45]==
ATTR Env: Environment;
ATTR Key: DefTableKey;
This macro is invoked in definition 7.
Scope Violation Rules[46]==
SYMBOL VarIdDef        INHERITS MultDefChk END;
SYMBOL FormalParameter INHERITS MultDefChk END;
SYMBOL Program         INHERITS RangeUnique END;
This macro is defined in definitions 30, 46, and 102.
This macro is invoked in definition 7.

2.8 Values and Types

Certain syntactic units are said to possess values. The various ``types'' (integer, real, Boolean) basically denote properties of these values. They are represented internally by the values OilTypeIntegerKey, OilTypeRealKey and OilTypeBooleanKey resulting from the OIL specification of the operator signatures.

The Revised Report does not consider that labels and strings have values, but this position is inconsistent with the fact that labels and strings can be passed as parameters. For uniformity, therefore, we specify operators for labels and strings as well as for integer, real and Boolean values. These specifications result in the values OilTypeLabelKey and OilTypeStringKey, which we use to represent the ``types'' of labels and strings.

Parameter-passing and assignment operations do not yield results, but rather have side effects on the state of the computation. Our specifications of these operations result in the value OilTypeVoidKey, and we use that value to represent situations in which no value may occur.

3 Expressions

The grammar given in the Revised Report is ambiguous. Several changes were needed to eliminate the ambiguity; these will be pointed out in the appropriate sections.

Because type information is required to distinguish arithmetic, Boolean and designational expressions, we have merged the grammars for these three kinds of expression. Wherever possible, however, we have retained the nonterminal symbols used in the Revised Report.

The Revised Report distinguishes various classes of identifier: variable identifier, array identifier, switch identifier and procedure identifier. Without information about how an identifier was declared, it is impossible to make this distinction in all contexts. We have therefore replaced every occurrence of the nonterminals variable identifier, array identifier, switch identifier and procedure identifier with the terminal Identifier and eliminated the rules defining those nonterminals.

Expressions[47]==
Arithmetic Expressions[64]
Boolean Expressions[75]
Designational Expressions[82]
This macro is invoked in definition 2.

3.1 Variables

3.1.1 Syntax

Because the phrase structure cannot distinguish different types of expression, we replace <arithmetic expression> here with Expression.
Variables[48]==
SimpleVariable ::= Identifier .
SubscriptExpression ::= Expression .
SubscriptList ::=
  SubscriptExpression /
  SubscriptList ',' SubscriptExpression .
SubscriptedVariable ::= Identifier '[' SubscriptList ']' .
Variable ::= SimpleVariable / SubscriptedVariable .
This macro is invoked in definition 2.

3.1.2 Equivalences

Equivalences[49]==
Variable ::= SimpleVariable SubscriptedVariable .
This macro is defined in definitions 49, 65, 76, and 105.
This macro is invoked in definition 6.

3.1.3 Semantics

The type of the value of a particular variable is defined in the declaration for the variable:
Semantics[50]==
SYMBOL Variable INHERITS IdUseEnv COMPUTE
  SYNT.Sym=TERM;
  SYNT.type=
    GetType(THIS.Key,OilErrorType())
    DEPENDS_ON INCLUDING Program.GotAllProperties;
END;
This macro is defined in definitions 44, 50, 57, 66, 77, 83, 85, 87, 91, 98, 106, 109, 112, and 117.
This macro is invoked in definition 7.
Properties characterizing all quantities[51]==
Type: tOilType;
This macro is defined in definitions 43 and 51.
This macro is invoked in definition 15.

3.1.4 Subscripts

Subscripts[52]==
ATTR dim, sub: int;

RULE: Variable ::= Identifier '[' SubscriptList ']'
COMPUTE
  .kind=
    GetKind(Variable.Key,UndefinedIdn)
    DEPENDS_ON INCLUDING Program.GotAllProperties;
  .dim=GetDim(Variable.Key,0) DEPENDS_ON INCLUDING Program.GotAllProperties;
  .sub=
    CONSTITUENTS SubscriptExpression.operator SHIELD SubscriptExpression
      WITH (int, ADD, ARGTOONE, ZERO);

  IF(AND(AND(NE(.kind,ArrayIdn),NE(.kind,UndefinedIdn)),NE(.kind,SwitchIdn)),
    message(ERROR,"Array or switch identifier required",0,COORDREF));
  IF(AND(NE(.dim,0),NE(.dim,.sub)),
    message(ERROR,"Number of indices differs from dimension",0,COORDREF));
END;

RULE: SubscriptExpression ::= Expression
COMPUTE
  SubscriptExpression.operator=
    OilIdOp2(OilOpColonEqual,OilTypeIntegerKey,Expression.type);
  IF(EQ(SubscriptExpression.operator,OilErrorOp()),
    message(ERROR,"Invalid type for subscript",0,COORDREF));
END;
This macro is invoked in definition 7.

3.2 Function Designators

3.2.1 Syntax

Function Designators[53]==
ActualParameter ::= String / Expression .
ParameterDelimiter ::= ',' / PDLetterString .
This macro is defined in definitions 53, 55, and 56.
This macro is invoked in definition 2.
The complex parameter delimiter must be treated as a basic symbol in order to distinguish the letter string it contains from an identifier:
Parameter Delimiter Letter String[54]==
PDLetterString : $\)[a-zA-Z]+:\(
This macro is invoked in definition 4.
Function Designators[55]==
ActualParameterList ::=
  ActualParameter /
  ActualParameterList ParameterDelimiter ActualParameter .
This macro is defined in definitions 53, 55, and 56.
This macro is invoked in definition 2.
A FunctionDesignator with an empty ActualParameterPart cannot be distinguished from a Variable in the absence of type information, so we have eliminated the empty alternative of the ActualParameterPart:
Function Designators[56]==
FunctionDesignator ::= Identifier '(' ActualParameterList ')' .
This macro is defined in definitions 53, 55, and 56.
This macro is invoked in definition 2.

3.2.2 Equivalences

3.2.3 Semantics

Semantics[57]==
SYMBOL FunctionDesignator INHERITS IdUseEnv, ActualFormalCorrespondence COMPUTE
  SYNT.Sym=TERM;
END;

RULE: FunctionDesignator ::= Identifier '(' ActualParameterList ')'
COMPUTE
  .kind=
    GetKind(FunctionDesignator.Key,UndefinedIdn)
    DEPENDS_ON INCLUDING Program.GotAllProperties;

  IF(AND(NE(.kind,ProcedureIdn),NE(.kind,UndefinedIdn)),
    message(ERROR, "Not a procedure identifier", 0, COORDREF));
END;
This macro is defined in definitions 44, 50, 57, 66, 77, 83, 85, 87, 91, 98, 106, 109, 112, and 117.
This macro is invoked in definition 7.

3.2.4 Standard Functions

Standard Functions[58]==
PreDefKey("abs",   absKey)
PreDefKey("sign",  signKey)
PreDefKey("sqrt",  sqrtKey)
PreDefKey("sin",   sinKey)
PreDefKey("cos",   cosKey)
PreDefKey("arctan",arctanKey)
PreDefKey("ln",    lnKey)
PreDefKey("exp",   expKey)
This macro is invoked in definition 19.
These functions are understood to operate indifferently on arguments both of type real and integer. They will all yield values of type real, except for sign(E) which will have values of type integer.
Define operators and associate them with indications[59]==
OPER
  absKey(RealKey): RealKey;
  signKey(RealKey): IntegerKey;
  sqrtKey,
  sinKey,
  cosKey,
  arctanKey,
  lnKey,
  expKey(RealKey): RealKey;
INDICATION
  absKey:    absKey;
  signKey:   signKey;
  sqrtKey:   sqrtKey;
  sinKey:    sinKey;
  cosKey:    cosKey;
  arctanKey: arctanKey;
  lnKey:     lnKey;
  expKey:    expKey;
This macro is defined in definitions 59, 62, 68, 70, 72, 74, 79, 81, and 88.
This macro is invoked in definition 12.
Properties of standard functions[60]==
absKey    -> Arity={1}, Operator={OilOpabsKey};
signKey   -> Arity={1}, Operator={OilOpsignKey};
sqrtKey   -> Arity={1}, Operator={OilOpsqrtKey};
sinKey    -> Arity={1}, Operator={OilOpsinKey};
cosKey    -> Arity={1}, Operator={OilOpcosKey};
arctanKey -> Arity={1}, Operator={OilOparctanKey};
lnKey     -> Arity={1}, Operator={OilOplnKey};
expKey    -> Arity={1}, Operator={OilOpexpKey};
This macro is invoked in definition 15.

3.2.5 Transfer Functions

Transfer Functions[61]==
PreDefKey("entier",entierKey)
This macro is invoked in definition 19.
Define operators and associate them with indications[62]==
OPER
  entierKey(RealKey): IntegerKey;
INDICATION
  entierKey: entierKey;
This macro is defined in definitions 59, 62, 68, 70, 72, 74, 79, 81, and 88.
This macro is invoked in definition 12.
Arity of transfer functions[63]==
entierKey -> Arity={1}, Operator={OilOpentierKey};
This macro is invoked in definition 15.

3.3 Arithmetic Expressions

3.3.1 Syntax

We have replaced the integer division operator of the reference language with the symbol div because of character set limitations:
Arithmetic Expressions[64]==
AddingOperator ::= '+' / '-' .
MultiplyingOperator ::= '*' / '/' / 'div' .
Primary ::=
  UnsignedNumber /
  Variable /
  FunctionDesignator /
  '(' Expression ')' .
Factor ::= Primary / Factor '^' Primary .
Term ::= Factor / Term MultiplyingOperator Factor .
SimpleArithmeticExpression ::=
  Term /
  '+' Term / '-' Term /
  SimpleArithmeticExpression AddingOperator Term .
IfClause ::= 'if' Expression 'then' .
This macro is defined in definitions 64.
This macro is invoked in definition 47.
The Revised Report makes no syntactic distinction between the monadic and dyadic versions of + and -. Because this distinction is important for the semantics of the language, we have made it by using literals for the monadic versions.

3.3.2 Equivalences

Equivalences[65]==
Binop ::= AddingOperator MultiplyingOperator .
Expression ::= SimpleArithmeticExpression Term Factor Primary UnsignedNumber .
This macro is defined in definitions 49, 65, 76, and 105.
This macro is invoked in definition 6.

3.3.3 Semantics

An arithmetic expression is a rule for computing a numerical value.
Semantics[66]==
RULE: ArithmeticExpression ::= Expression
COMPUTE
  IF(
    AND(AND(
      NE(Expression.type,OilErrorType()),
      NE(Expression.type,OilTypeIntegerKey)),
      NE(Expression.type,OilTypeRealKey)),
    message(ERROR,"Expression must be arithmetic",0,COORDREF));
END;

RULE: IfClause ::= 'if' BooleanExpression 'then' END;

Denotation types[39]

RULE: Expression ::= Variable
COMPUTE
  Expression.type = Variable.type;
END;

RULE: Expression ::= FunctionDesignator
COMPUTE
  Expression.type = FunctionDesignator.type;
END;
This macro is defined in definitions 44, 50, 57, 66, 77, 83, 85, 87, 91, 98, 106, 109, 112, and 117.
This macro is invoked in definition 7.

3.3.4 Operators and Types

Apart from the Boolean expressions of if clauses, the constituents of simple arithmetic expressions must be of types real or integer. The meanings of the basic operators and the types of the expressions to which they lead are given by the rules in this section.

The operators +, - and * have the conventional meaning (addition, subtraction and multiplication). The type of the expression will be integer if both of the operands are of integer type, otherwise real:

Establish operator indications for source symbols[67]==
Set a monadic operator indication[10] (`+', `Nop')
Set a dyadic operator indication[11] (`+', `Plus')
Set a monadic operator indication[10] (`-', `Neg')
Set a dyadic operator indication[11] (`-', `Minus')
Set a dyadic operator indication[11] (`*', `Star')
This macro is defined in definitions 67, 69, 71, 78, and 80.
This macro is invoked in definition 7.
Define operators and associate them with indications[68]==
OPER
  iNop,  iNeg(IntegerKey):                               IntegerKey;
  iiAdd, iiSubtract, iiMultiply(IntegerKey, IntegerKey): IntegerKey;
  rNop,  rNeg(RealKey):                                  RealKey;
  rrAdd, rrSubtract, rrMultiply(RealKey, RealKey):       RealKey;
INDICATION
  Nop:   iNop, rNop;
  Plus:  iiAdd, rrAdd;
  Neg:   iNeg, rNeg;
  Minus: iiSubtract, rrSubtract;
  Star:  iiMultiply, rrMultiply;
This macro is defined in definitions 59, 62, 68, 70, 72, 74, 79, 81, and 88.
This macro is invoked in definition 12.
It is unnecessary to provide operators for all combinations of integer and real operands because Section 5.1.3 implies that the compiler is allowed to convert an integer value to a real value in an arithmetic expression wherever the context requires it.

The source language operator symbols / and div both denote division, to be understood as a multiplication of the left operand by the reciprocal of the right operand.

/ is defined for all four combinations of types real and integer and will yield results of type real in any case. The operator div is defined only for two operands both of type integer and will yield a result of type integer:

Establish operator indications for source symbols[69]==
Set a dyadic operator indication[11] (`/', `Slash')
Set a dyadic operator indication[11] (`div', `Div')
This macro is defined in definitions 67, 69, 71, 78, and 80.
This macro is invoked in definition 7.
Define operators and associate them with indications[70]==
OPER
  rrDiv(RealKey, RealKey):       RealKey;
  iiDiv(IntegerKey, IntegerKey): IntegerKey;
INDICATION
  Slash: rrDiv;
  Div:   iiDiv;
This macro is defined in definitions 59, 62, 68, 70, 72, 74, 79, 81, and 88.
This macro is invoked in definition 12.
The source language operator symbols ^ denotes exponentiation:
Establish operator indications for source symbols[71]==
Set a dyadic operator indication[11] (`^', `UpArrow')
This macro is defined in definitions 67, 69, 71, 78, and 80.
This macro is invoked in definition 7.
Define operators and associate them with indications[72]==
OPER
  iiExp(IntegerKey, IntegerKey): ArithKey;
  riExp(RealKey,    IntegerKey): RealKey;
  rrExp(RealKey,       RealKey): RealKey;
INDICATION
  UpArrow: iiExp, riExp, rrExp;
This macro is defined in definitions 59, 62, 68, 70, 72, 74, 79, 81, and 88.
This macro is invoked in definition 12.
The fact that the type of the result of an exponentiation depends on the value of one of the operands is a problem, because type is considered to be a static property of the program in this specification. A new arithmetic type is defined to handle this situation.

A value of arithmetic type will have to carry an indication of its type with it. It can be used as either a value of type integer or a value of type real, subject to appropriate run-time checks and/or conversions:

Define an operator to be applied when needed[73]==
COERCION
  aiConvert(ArithKey): IntegerKey;
  arConvert(ArithKey): RealKey;
This macro is defined in definitions 73 and 107.
This macro is invoked in definition 12.
Unfortunately, it must be possible to perform all arithmetic operations on values of arithmetic type if those operations can be performed on values of either type real or type integer:
Define operators and associate them with indications[74]==
OPER
  aNop, aNeg(ArithKey):                                ArithKey;
  aiAdd, aiSubtract, aiMultiply(ArithKey, IntegerKey): ArithKey;
  iaAdd, iaSubtract, iaMultiply(IntegerKey, ArithKey): ArithKey;
  aaAdd, aaSubtract, aaMultiply(ArithKey, ArithKey):   ArithKey;
  aiExp(ArithKey, IntegerKey): ArithKey;
  iaExp(IntegerKey, ArithKey): ArithKey;
  arExp(ArithKey,    RealKey): RealKey;
  raExp(RealKey,    ArithKey): RealKey;
  aaExp(ArithKey,   ArithKey): ArithKey;
INDICATION
  Nop:     aNop;
  Plus:    aiAdd, iaAdd, aaAdd;
  Neg:     aNeg;
  Minus:   aiSubtract, iaSubtract, aaSubtract;
  Star:    aiMultiply, iaMultiply, aaMultiply;
  UpArrow: aiExp, iaExp, arExp, raExp, aaExp;
This macro is defined in definitions 59, 62, 68, 70, 72, 74, 79, 81, and 88.
This macro is invoked in definition 12.

3.4 Boolean Expressions

3.4.1 Syntax

We used obvious representations for the operators that are not available as ASCII characters:
Boolean Expressions[75]==
RelationalOperator ::= '<' / '<=' / '=' / '>=' / '>' / '!=' .
Relation ::=
  SimpleArithmeticExpression RelationalOperator
  SimpleArithmeticExpression .
BooleanPrimary ::=
  SimpleArithmeticExpression /
  LogicalValue /
  Relation .
BooleanSecondary ::= BooleanPrimary / 'not' BooleanPrimary .
BooleanFactor ::= BooleanSecondary /
                  BooleanFactor 'and' BooleanSecondary .
BooleanTerm ::= BooleanFactor / BooleanTerm 'or' BooleanFactor .
Implication ::= BooleanTerm / Implication '->' BooleanTerm .
SimpleBoolean ::= Implication / SimpleBoolean '==' Implication .
Expression ::=
  SimpleBoolean /
  IfClause SimpleBoolean 'else' Expression .
This macro is invoked in definition 47.

3.4.2 Equivalences

Equivalences[76]==
Binop ::= RelationalOperator .
Expression ::=
  SimpleBoolean Implication BooleanTerm BooleanFactor BooleanSecondary
  BooleanPrimary Relation LogicalValue.
This macro is defined in definitions 49, 65, 76, and 105.
This macro is invoked in definition 6.

3.4.3 Semantics

A Boolean expression is a rule for computing a logical value.
Semantics[77]==
RULE: BooleanExpression ::= Expression
COMPUTE
  IF(
    AND(
      NE(Expression.type,OilErrorType()),
      NE(Expression.type,OilTypeBooleanKey)),
    message(ERROR,"Expression must be Boolean",0,COORDREF));
END;

RULE: Expression ::= 'true'
COMPUTE
  Expression.type=OilTypeBooleanKey;
END;

RULE: Expression ::= 'false'
COMPUTE
  Expression.type=OilTypeBooleanKey;
END;
This macro is defined in definitions 44, 50, 57, 66, 77, 83, 85, 87, 91, 98, 106, 109, 112, and 117.
This macro is invoked in definition 7.

3.4.4 Types

3.4.5 The Operators

Relations take on the value true whenever the corresponding relation is satisfied for the expressions involved, otherwise false.
Establish operator indications for source symbols[78]==
Set a monadic operator indication[10] (`not', `Not')
Set a dyadic operator indication[11] (`<', `Lt')
Set a dyadic operator indication[11] (`<=', `Le')
Set a dyadic operator indication[11] (`=', `Eq')
Set a dyadic operator indication[11] (`!=', `Ne')
Set a dyadic operator indication[11] (`>=', `Ge')
Set a dyadic operator indication[11] (`>', `Gt')
This macro is defined in definitions 67, 69, 71, 78, and 80.
This macro is invoked in definition 7.
Define operators and associate them with indications[79]==
OPER
  iiLT, iiLE, iiEQ, iiGE, iiGT, iiNE(IntegerKey, IntegerKey): BooleanKey;
  rrLT, rrLE, rrEQ, rrGE, rrGT, rrNE(RealKey, RealKey):       BooleanKey;
INDICATION
  Lt: iiLT, rrLT;
  Le: iiLE, rrLE;
  Eq: iiEQ, rrEQ;
  Ge: iiGE, rrGE;
  Gt: iiGT, rrGT;
  Ne: iiNE, rrNE;
This macro is defined in definitions 59, 62, 68, 70, 72, 74, 79, 81, and 88.
This macro is invoked in definition 12.
The logical operators always take values of type Boolean as operands and yield values of type Boolean as results.
Establish operator indications for source symbols[80]==
Set a dyadic operator indication[11] (`and', `And')
Set a dyadic operator indication[11] (`or', `Or')
Set a dyadic operator indication[11] (`->', `Implies')
Set a dyadic operator indication[11] (`==', `Equiv')
This macro is defined in definitions 67, 69, 71, 78, and 80.
This macro is invoked in definition 7.
Define operators and associate them with indications[81]==
OPER
  bNot(BooleanKey): BooleanKey;
  bAnd, bOr, bImplies, bEquiv(BooleanKey, BooleanKey): BooleanKey;
INDICATION
  Not:     bNot;
  And:     bAnd;
  Or:      bOr;
  Implies: bImplies;
  Equiv:   bEquiv;
This macro is defined in definitions 59, 62, 68, 70, 72, 74, 79, 81, and 88.
This macro is invoked in definition 12.

3.5 Designational Expressions

3.5.1 Syntax

We do not allow unsigned integer labels because of an ambiguity that arises when a designational expression is used as an actual parameter. (This ambiguity was reported in ALGOL Bulletin No. 10, October, 1960.)
Designational Expressions[82]==
Label ::= Identifier .
This macro is invoked in definition 47.
Designational expressions do not appear in the grammar because their phrase structure is already covered by other forms of expression, and they cannot be distinguished without type information.

3.5.2 Equivalences

3.5.3 Semantics

Semantics[83]==
SYMBOL Label INHERITS IdDefScope COMPUTE
  SYNT.Sym=TERM;
  SYNT.GotProperties=
    ORDER(ResetType(THIS.Key,OilTypeLabelKey),ResetKind(THIS.Key,LabelIdn));
END;

RULE: DesignationalExpression ::= Expression
COMPUTE
  IF(
    AND(
      NE(Expression.type,OilErrorType()),
      NE(Expression.type,OilTypeLabelKey)),
    message(ERROR,"Label expected",0,COORDREF));
END;
This macro is defined in definitions 44, 50, 57, 66, 77, 83, 85, 87, 91, 98, 106, 109, 112, and 117.
This macro is invoked in definition 7.

4 Statements

All of the semicolons must be replaced by the terminal Semi, in order to obtain the comment behavior discussed in Section 2.3.

4.1 Compound Statements and Blocks

4.1.1 Syntax

Compound Statements and Blocks[84]==
UnlabelledBasicStatement ::=
  AssignmentStatement /
  GoToStatement /
  DummyStatement /
  ProcedureStatement .
BasicStatement ::= UnlabelledBasicStatement / Label ':' BasicStatement.
UnconditionalStatement ::= BasicStatement / CompoundStatement / Block .
Statement ::= UnconditionalStatement / ConditionalStatement / ForStatement .
CompoundTail ::= Statement end / Statement Semi CompoundTail .
BlockHead ::= begin Declaration / BlockHead Semi Declaration .
UnlabelledCompound ::= begin CompoundTail .
UnlabelledBlock ::= BlockHead Semi CompoundTail .
CompoundStatement ::= UnlabelledCompound / Label ':' CompoundStatement .
Block ::= UnlabelledBlock / Label ':' Block .
Program ::= Block / CompoundStatement .
This macro is invoked in definition 2.

4.1.2 Equivalences

4.1.3 Semantics

Every block automatically introduces a new level of nomenclature. This is realized as follows: Any identifier occurring within the block may through a suitable declaration be specified to be local to the block in question.
Semantics[85]==
SYMBOL Block INHERITS RangeScope END;
This macro is defined in definitions 44, 50, 57, 66, 77, 83, 85, 87, 91, 98, 106, 109, 112, and 117.
This macro is invoked in definition 7.

4.2 Assignment Statements

4.2.1 Syntax

The nonterminals <arithmetic expression> and <Boolean expression> must be replaced by Expression, in accordance with the changes made in Section 3. Also, as discussed in Section 3, distinctions among identifiers must be dropped. Therefore there is only a single rule for LeftPart and a single rule for AssignmentStatement:
Assignment Statements[86]==
LeftPart ::= Variable ':=' .
LeftPartList ::= LeftPart / LeftPartList LeftPart .
AssignmentStatement ::= LeftPartList Expression .
This macro is invoked in definition 2.

4.2.2 Equivalences

4.2.3 Semantics

Semantics[87]==
ATTR Key: DefTableKey;

SYMBOL Program COMPUTE
  THIS.Key = NoKey;
END;

RULE: LeftPart ::= Variable ':=' COMPUTE
  LeftPart.kind=
    GetKind(Variable.Key,UndefinedIdn)
    DEPENDS_ON INCLUDING Program.GotAllProperties;
  IF(
    AND(AND(
      NE(LeftPart.kind,VariableIdn),
      NE(LeftPart.kind,ArrayIdn)),
      NE(LeftPart.kind,ProcedureIdn)),
    message(ERROR,"Illegal left of :=",0,COORDREF));
  IF(
    AND(
      EQ(LeftPart.kind,ProcedureIdn),
      NE(INCLUDING (ProcedureDeclaration.Key,Program.Key),Variable.Key)),
    message(ERROR,"Illegal use of procedure id",0,COORDREF));
END;
This macro is defined in definitions 44, 50, 57, 66, 77, 83, 85, 87, 91, 98, 106, 109, 112, and 117.
This macro is invoked in definition 7.

4.2.4 Types

Define operators and associate them with indications[88]==
OPER
   bAssign(BooleanKey,BooleanKey):VoidKey;
  iiAssign(IntegerKey,IntegerKey):VoidKey;
  irAssign(IntegerKey,RealKey):   VoidKey;
  rrAssign(RealKey,RealKey):      VoidKey;
INDICATION
  ColonEqual: bAssign, iiAssign, irAssign, rrAssign;
This macro is defined in definitions 59, 62, 68, 70, 72, 74, 79, 81, and 88.
This macro is invoked in definition 12.
Verify type consistency of a left-part list[89]==
CHAIN LeftPartType: tOilType;

RULE: AssignmentStatement ::= LeftPartList Expression
COMPUTE
  .operator=OilIdOp2(OilOpColonEqual,AssignmentStatement.type,Expression.type);

  CHAINSTART LeftPartList.LeftPartType=OilErrorType();
  AssignmentStatement.type=LeftPartList.LeftPartType;

  IF(NOT(OilIsValidOp(.operator)),
    message(ERROR, "Illegal assignment", 0, COORDREF));
END;

RULE: LeftPart ::= Variable ':=' COMPUTE
  LeftPart.LeftPartType=
    IF(EQ(LeftPart.LeftPartType,OilErrorType()),
      Variable.type,
      ORDER(
        IF(NE(Variable.type,LeftPart.LeftPartType),
          message(ERROR,"Type differs from earlier left part",0,COORDREF)),
        LeftPart.LeftPartType));
END;
This macro is invoked in definition 7.

4.3 Go To Statements

4.3.1 Syntax

The nonterminal <designational expression> must be replaced by Expression, in accordance with the changes made in Section 3.
Go To Statements[90]==
GoToStatement ::= 'go' 'to' Expression .
This macro is invoked in definition 2.

4.3.2 Equivalences

4.3.3 Semantics

Semantics[91]==
RULE: GoToStatement ::= 'go' 'to' DesignationalExpression END;
This macro is defined in definitions 44, 50, 57, 66, 77, 83, 85, 87, 91, 98, 106, 109, 112, and 117.
This macro is invoked in definition 7.
Define operators to establish types without values[92]==
OPER
  jump(LabelKey):VoidKey;
This macro is defined in definitions 92 and 101.
This macro is invoked in definition 12.

4.4 Dummy Statements

Dummy Statements[93]==
DummyStatement ::= Empty .
This macro is invoked in definition 2.

4.5 Conditional Statements

We have deleted the definition of IfClause, which duplicates the one in Section 3, and the definition of UnconditionalStatement, which duplicates the one in Section 4.1.
Conditional Statements[94]==
IfStatement ::= IfClause UnconditionalStatement .
ConditionalStatement ::=
  IfStatement /
  IfStatement 'else' Statement /
  IfClause ForStatement /
  Label ':' ConditionalStatement .
This macro is invoked in definition 2.

4.6 For Statements

4.6.1 Syntax

The nonterminals <arithmetic expression> and <Boolean expression> must be replaced by Expression, in accordance with the changes made in Section 3.
For Statements[95]==
ForListElement ::=
  Expression /
  Expression 'step' Expression 'until' Expression /
  Expression 'while' Expression .
ForList ::= ForListElement / ForList ',' ForListElement .
ForClause ::= 'for' Variable ':=' ForList 'do' .
ForStatement ::= ForClause Statement / Label ':' ForStatement .
This macro is invoked in definition 2.

4.6.2 Equivalences

4.6.3 Semantics

4.6.4 The for List Elements

The kinds of expression that can appear in for list elements are constrained as follows:
The for List Elements[96]==
RULE: ForListElement ::= ArithmeticExpression END;

RULE: ForListElement ::=
  ArithmeticExpression 'step' ArithmeticExpression 'until' ArithmeticExpression
END;

RULE: ForListElement ::= ArithmeticExpression 'while' BooleanExpression END;
This macro is invoked in definition 7.

4.7 Procedure Statements

4.7.1 Syntax

We have deleted the definitions of <actual parameter>, <parameter delimiter>, <actual parameter list> and <actual parameter part>. All of these nonterminals are defined in Section 3.2. (See the discussion there concerning the ambiguity of a letter string that is part of a parameter delimiter.) In addition, <procedure identifier> must be replaced by Identifier, because we cannot distinguish identifiers on the basis of type in the grammar.
Procedure Statements[97]==
ActualParameterPart ::= Empty / '(' ActualParameterList ')' .
ProcedureStatement ::= Identifier ActualParameterPart .
This macro is invoked in definition 2.

4.7.2 Equivalences

4.7.3 Semantics

Semantics[98]==
SYMBOL ProcedureStatement INHERITS IdUseEnv, ActualFormalCorrespondence COMPUTE
  SYNT.Sym=TERM;
  IF(NE(THIS.Key,NoKey),
    IF(NE(GetKind(THIS.Key,UndefinedIdn),ProcedureIdn),
      message(ERROR,CatStrInd("Not a procedure: ",TERM),0,COORDREF),
    IF(NE(THIS.type,OilTypeVoidKey),
      message(ERROR,"Type procedure illegal here",0,COORDREF))))
    DEPENDS_ON INCLUDING Program.GotAllProperties;
END;
This macro is defined in definitions 44, 50, 57, 66, 77, 83, 85, 87, 91, 98, 106, 109, 112, and 117.
This macro is invoked in definition 7.

4.7.4 Actual-Formal Correspondence

The actual parameter list of the procedure statement must have the same number of entries as the formal parameter list of the procedure declaration heading.
Actual-Formal Correspondence[99]==
CHAIN cnt: int;
ATTR Arity: int;

SYMBOL ActualFormalCorrespondence COMPUTE
  CHAINSTART HEAD.cnt = 0;
  SYNT.Arity=
    GetArity(THIS.Key,SUB(0,1))
    DEPENDS_ON INCLUDING Program.GotAllProperties;
  IF(NE(THIS.Arity,TAIL.cnt),
      message(ERROR, "Number of parameters mismatch", 0, COORDREF));
END;

SYMBOL ActualParameter COMPUTE
  THIS.cnt=ADD(THIS.cnt,1);
END;
This macro is invoked in definition 7.

4.7.5 Restrictions

Restrictions[100]==
ATTR Arguments, TrailArgs: tOilSetSig;

SYMBOL ActualFormalCorrespondence COMPUTE
  SYNT.operator=
    IF(EQ(THIS.Arity,TAIL.cnt),
      OilIdOpTSn(
        GetOperator(THIS.Key,OilErrorOp()),
        THIS.Arguments,
        OilErrorType()),
      OilErrorOp())
    DEPENDS_ON INCLUDING Program.GotAllProperties;
  SYNT.type=OilGetArgType(THIS.operator,0);

  IF(NOT(OilIsValidOp(THIS.operator)),
    message(ERROR,"Argument type mismatch",0,COORDREF));
END;

RULE: ProcedureStatement ::= Identifier ActualParameterPart
COMPUTE
  ProcedureStatement.Arguments=ActualParameterPart.Arguments;
END;

RULE: ActualParameterPart ::= Empty
COMPUTE
  ActualParameterPart.Arguments=OilNewSetSig();
END;

RULE: ActualParameterPart ::= '(' ActualParameterList ')'
COMPUTE
  ActualParameterPart.Arguments=ActualParameterList.Arguments;
  ActualParameterList.TrailArgs=OilNewSetSig();
END;

RULE: FunctionDesignator ::= Identifier '(' ActualParameterList ')'
COMPUTE
  FunctionDesignator.Arguments=ActualParameterList.Arguments;
  ActualParameterList.TrailArgs=OilNewSetSig();
END;

RULE: ActualParameterList ::=
  ActualParameterList ParameterDelimiter ActualParameter
COMPUTE
  ActualParameterList[1].Arguments=ActualParameterList[2].Arguments;
  ActualParameterList[2].TrailArgs=
    OilAddSetSig(
      OilTypeToSet(ActualParameter.type),
      ActualParameterList[1].TrailArgs);
END;

RULE: ActualParameterList ::= ActualParameter
COMPUTE
  ActualParameterList.Arguments=
    OilAddSetSig(
      OilTypeToSet(ActualParameter.type),
      ActualParameterList.TrailArgs);
END;

RULE: ActualParameter ::= String
COMPUTE
  ActualParameter.type=OilTypeStringKey;
END;

RULE: ActualParameter ::= Expression
COMPUTE
  ActualParameter.type=Expression.type;
END;
This macro is invoked in definition 7.
Define operators to establish types without values[101]==
OPER
  pass(StringKey):VoidKey;
This macro is defined in definitions 92 and 101.
This macro is invoked in definition 12.

5 Declarations

Apart from labels, all identifiers of a program must be declared.
Scope Violation Rules[102]==
SYMBOL IdUseEnv COMPUTE
   IF( EQ(THIS.Key, NoKey),
       message(ERROR, CatStrInd("Unknown identifier: ", THIS.Sym),
               0, COORDREF));
END;
This macro is defined in definitions 30, 46, and 102.
This macro is invoked in definition 7.
Declarations[103]==
Declaration ::=
  TypeDeclaration /
  ArrayDeclaration /
  SwitchDeclaration /
  ProcedureDeclaration .

Type Declarations[104]
Array Declarations[108]
Switch Declarations[111]
Procedure Declarations[113]
This macro is invoked in definition 2.

5.1 Type Declarations

5.1.1 Syntax

The Revised Report defines the elements of a TypeList to be SimpleVariables. This definition does not reflect the difference in semantics between definition and use. In order to expose that difference, we have used the symbol VarIdDef here.
Type Declarations[104]==
TypeList ::= VarIdDef / VarIdDef ',' TypeList .
VarIdDef ::= Identifier .
Type ::= 'real' / 'integer' / 'Boolean' .
LocalOrOwnType ::= Type / 'own' Type .
TypeDeclaration ::= LocalOrOwnType TypeList .
This macro is invoked in definition 103.

5.1.2 Equivalences

Equivalences[105]==
Type ::= LocalOrOwnType .
This macro is defined in definitions 49, 65, 76, and 105.
This macro is invoked in definition 6.

5.1.3 Semantics

Semantics[106]==
SYMBOL VarIdDef INHERITS IdDefScope COMPUTE
  SYNT.Sym=TERM;
  SYNT.GotProperties=
    ORDER(
      ResetKind(THIS.Key,VariableIdn),
      ResetType(THIS.Key,INCLUDING TypeDeclaration.type));
END;

RULE: Type ::= 'real'
COMPUTE
  Type.type=OilTypeRealKey;
END;

RULE: Type ::= 'integer'
COMPUTE
  Type.type=OilTypeIntegerKey;
END;

RULE: Type ::= 'Boolean'
COMPUTE
  Type.type=OilTypeBooleanKey;
END;

SYMBOL TypeDeclaration COMPUTE
  SYNT.type=CONSTITUENT Type.type;
END;
This macro is defined in definitions 44, 50, 57, 66, 77, 83, 85, 87, 91, 98, 106, 109, 112, and 117.
This macro is invoked in definition 7.
In arithmetic expressions any position which can be occupied by a real declared variable may be occupied by an integer declared variable.
Define an operator to be applied when needed[107]==
COERCION
  irConvert(IntegerKey): RealKey;
This macro is defined in definitions 73 and 107.
This macro is invoked in definition 12.

5.2 Array Declarations

5.2.1 Syntax

The nonterminal <arithmetic expression> is replaced by Expression and array identifiers are not distinguished, as discussed in Section 3.
Array Declarations[108]==
LowerBound ::= Expression .
UpperBound ::= Expression .
BoundPair ::= LowerBound ':' UpperBound .
BoundPairList ::= BoundPair / BoundPairList ',' BoundPair .
ArraySegment ::=
  Identifier '[' BoundPairList ']' / Identifier ',' ArraySegment .
ArrayList ::= ArraySegment / ArrayList ',' ArraySegment .
ArrayDeclaration ::= 'array' ArrayList / LocalOrOwnType 'array' ArrayList .
This macro is invoked in definition 103.

5.2.2 Equivalences

5.2.3 Semantics

Semantics[109]==
ATTR Dimension: int;

SYMBOL ArraySegment INHERITS IdDefScope COMPUTE
  SYNT.Sym=TERM;
  SYNT.GotProperties=
    ORDER(
      ResetKind(THIS.Key,ArrayIdn),
      ResetType(THIS.Key,INCLUDING ArrayDeclaration.type),
      ResetDim(THIS.Key,THIS.Dimension));
END;

RULE: BoundPairList ::= BoundPair
COMPUTE
  BoundPairList.Dimension=1;
END;

RULE: BoundPairList ::= BoundPairList ',' BoundPair
COMPUTE
  BoundPairList[1].Dimension=ADD(BoundPairList[2].Dimension,1);
END;

RULE: ArraySegment ::= Identifier '[' BoundPairList ']'
COMPUTE
  ArraySegment.Dimension=BoundPairList.Dimension;
END;

RULE: ArraySegment ::= Identifier ',' ArraySegment
COMPUTE
  ArraySegment[1].Dimension=ArraySegment[2].Dimension;
END;

RULE: ArrayDeclaration ::= Type 'array' ArrayList
COMPUTE
  ArrayDeclaration.type=Type.type;
END;

RULE: ArrayDeclaration ::= 'array' ArrayList
COMPUTE
  ArrayDeclaration.type=OilTypeRealKey;
END;
This macro is defined in definitions 44, 50, 57, 66, 77, 83, 85, 87, 91, 98, 106, 109, 112, and 117.
This macro is invoked in definition 7.
Properties characterizing arrays[110]==
Dim: int;
This macro is invoked in definition 15.

5.3 Switch Declarations

5.3.1 Syntax

The nonterminal <designational expression> is replaced by Expression and switch identifiers are not distinguished, as discussed in Section 3.
Switch Declarations[111]==
SwitchList ::= Expression / SwitchList ',' Expression .
SwitchDeclaration ::= 'switch' Identifier ':=' SwitchList .
This macro is invoked in definition 103.

5.3.2 Equivalences

5.3.3 Semantics

Semantics[112]==
SYMBOL SwitchDeclaration INHERITS IdDefScope COMPUTE
  SYNT.Sym=TERM;
  SYNT.GotProperties=
    ORDER(
      ResetKind(THIS.Key,SwitchIdn),
      ResetType(THIS.Key,OilTypeLabelKey),
      ResetDim(THIS.Key,1));
END;

RULE: SwitchList ::= DesignationalExpression END;
RULE: SwitchList ::= SwitchList ',' DesignationalExpression END;
This macro is defined in definitions 44, 50, 57, 66, 77, 83, 85, 87, 91, 98, 106, 109, 112, and 117.
This macro is invoked in definition 7.

5.4 Procedure Declarations

5.4.1 Syntax

If there are no formal parameters, the ValuePart and the SpecificationPart must also be empty. This constraint is not enforced by the grammar of the Revised Report, and therefore cannot be checked syntactically. In order to remedy this defect, we have eliminated the empty production for FormalParameterPart and changed the definition of ProcedureHeading to reflect the necessary correspondence.
Procedure Declarations[113]==
FormalParameter ::= Identifier .
FormalParameterList ::=
  FormalParameter /
  FormalParameterList ParameterDelimiter FormalParameter .
FormalParameterPart ::= '(' FormalParameterList ')' .
IdentifierList ::= Identifier / IdentifierList ',' Identifier .
This macro is defined in definitions 113, 114, 115, and 116.
This macro is invoked in definition 103.
The Revised Report uses the nonterminal <identifier list> to represent uses of formal parameter identifiers in both the value part and the specification part. The semantics of these two contexts are quite different, so we defined another nonterminal, ValueParameterList to represent the former context.
Procedure Declarations[114]==
ValueParameterList ::= ValueParameter / ValueParameterList ',' ValueParameter .
ValueParameter ::= Identifier .
ValuePart ::= 'value' ValueParameterList Semi / Empty .
Specifier ::=
  'string' /
  Type /
  'array' /
  Type 'array' /
  'label' /
  'switch' /
  'procedure' /
  Type 'procedure' .
This macro is defined in definitions 113, 114, 115, and 116.
This macro is invoked in definition 103.
The Revised Report does not require type information for formal parameters. If no type information is available, however, it may be impossible for the compiler to determine the type of an expression. Thus we require that all formal parameters have specifications, which means that a SpecificationPart can be empty only if there are no formal parameters. That constraint is verified by the definition of the ProcedureHeading:
Procedure Declarations[115]==
SpecificationPart ::=
  Specifier IdentifierList Semi /
  SpecificationPart Specifier IdentifierList Semi .
This macro is defined in definitions 113, 114, 115, and 116.
This macro is invoked in definition 103.
The grammar given in the Revised Report does not reflect the scope rules of the language: A procedure identifier is declared in the enclosing scope, and the formal parameters are declared in the procedure body, but the grammar places both in the <procedure heading> phrase. We have therefore moved the procedure identifier to the procedure declaration and defined a new nonterminal, ProcedureRange, that is exactly the scope of the formal parameters described by Section 5.4.3 of the Revised Report:
Procedure Declarations[116]==
ProcedureHeading ::=
  FormalParameterPart Semi ValuePart SpecificationPart / Semi .
ProcedureBody ::= Statement / Code .
ProcedureRange ::=
  ProcedureHeading ProcedureBody .
ProcedureDeclaration ::=
  'procedure' Identifier ProcedureRange /
  Type 'procedure' Identifier ProcedureRange .
This macro is defined in definitions 113, 114, 115, and 116.
This macro is invoked in definition 103.

5.4.2 Equivalences

5.4.3 Semantics

Semantics[117]==
SYMBOL ProcedureDeclaration INHERITS IdDefScope COMPUTE
  SYNT.Sym=TERM;
  SYNT.GotProperties=
    ORDER(
      ResetKind(THIS.Key,ProcedureIdn),
      ResetArity(THIS.Key,THIS.Arity),
      ResetType(THIS.Key,THIS.type));
END;

RULE: ProcedureDeclaration ::= 'procedure' Identifier ProcedureRange
COMPUTE
  ProcedureDeclaration.type=OilTypeVoidKey;
END;

RULE: ProcedureDeclaration ::= Type 'procedure' Identifier ProcedureRange
COMPUTE
  ProcedureDeclaration.type=Type.type;
END;
This macro is defined in definitions 44, 50, 57, 66, 77, 83, 85, 87, 91, 98, 106, 109, 112, and 117.
This macro is invoked in definition 7.

5.4.4 Values of Function Designators

5.4.5 Specifications

Specifications[118]==
SYMBOL ProcedureDeclaration COMPUTE
   SYNT.GotFormalTypes=CONSTITUENTS IdentifierList.GotType;
   SYNT.Arity=CONSTITUENTS FormalParameter.Key WITH (int,ADD,ARGTOONE,ZERO);
END;

SYMBOL ProcedureRange INHERITS RangeScope END;

SYMBOL FormalParameter INHERITS IdDefScope COMPUTE
  SYNT.Sym=TERM;
END;

SYMBOL ValueParameter INHERITS IdUseScope COMPUTE
  SYNT.Sym=TERM;
  SYNT.GotProperties=ResetIsValue(THIS.Key, 1);
END;

SYMBOL SpecificationPart COMPUTE
   SYNT.kind=CONSTITUENT Specifier.kind;
   SYNT.type=CONSTITUENT Specifier.type;
END;

SYMBOL IdentifierList INHERITS IdUseScope COMPUTE
  SYNT.Sym=TERM;
  SYNT.GotType=
    ResetType(THIS.Key,INCLUDING SpecificationPart.type);
  SYNT.GotProperties=
    ResetKind(THIS.Key,INCLUDING SpecificationPart.kind)
    DEPENDS_ON THIS.GotType;
END;

RULE: Specifier ::= 'string'
COMPUTE
   Specifier.type=OilTypeStringKey;
   Specifier.kind=VariableIdn;
END;

RULE: Specifier ::= Type
COMPUTE
   Specifier.type=Type.type;
   Specifier.kind=VariableIdn;
END;

RULE: Specifier ::= 'array'
COMPUTE
   Specifier.type=OilTypeRealKey;
   Specifier.kind=ArrayIdn;
END;

RULE: Specifier ::= Type 'array'
COMPUTE
   Specifier.type=Type.type;
   Specifier.kind = ArrayIdn;
END;

RULE: Specifier ::= 'label'
COMPUTE
   Specifier.type=OilTypeLabelKey;
   Specifier.kind=LabelIdn;
END;

RULE: Specifier ::= 'switch'
COMPUTE
   Specifier.type=OilTypeLabelKey;
   Specifier.kind=SwitchIdn;
END;

RULE: Specifier ::= 'procedure'
COMPUTE
   Specifier.type=OilTypeRealKey;
   Specifier.kind=ProcedureIdn;
END;

RULE: Specifier ::= Type 'procedure'
COMPUTE
   Specifier.type=Type.type;
   Specifier.kind = ProcedureIdn;
END;

ATTR Signature, Trailing: tOilArgSig;

RULE: FormalParameterPart ::= '(' FormalParameterList ')'
COMPUTE
  FormalParameterPart.operator=
    OilNewOp(
      INCLUDING ProcedureDeclaration.Key,
      OilAddArgSig(
        INCLUDING ProcedureDeclaration.type,
        FormalParameterList.Signature),
      1);
  FormalParameterPart.GotProperties=
    ORDER(
      ResetOperator(
        INCLUDING ProcedureDeclaration.Key,
        FormalParameterPart.operator),
      OilAddIdentification(
        FormalParameterPart.operator,
        FormalParameterPart.operator));
  FormalParameterList.Trailing=OilNewArgSig();
END;

RULE: FormalParameterList ::=
  FormalParameterList ParameterDelimiter FormalParameter
COMPUTE
  FormalParameterList[1].Signature=FormalParameterList[2].Signature;
  FormalParameterList[2].Trailing=
    OilAddArgSig(
      GetType(FormalParameter.Key,OilErrorType()),
      FormalParameterList[1].Trailing)
    DEPENDS_ON INCLUDING ProcedureDeclaration.GotFormalTypes;
END;

RULE: FormalParameterList ::= FormalParameter
COMPUTE
  FormalParameterList.Signature=
    OilAddArgSig(
      GetType(FormalParameter.Key,OilErrorType()),
      FormalParameterList.Trailing)
    DEPENDS_ON INCLUDING ProcedureDeclaration.GotFormalTypes;
END;
This macro is invoked in definition 7.
Properties characterizing procedures[119]==
Operator: tOilOp;
Arity:    int;
This macro is invoked in definition 15.
Properties characterizing formal parameters[120]==
IsValue:  int;
This macro is invoked in definition 15.

5.4.6 Code as Procedure Body

It is understood that the procedure body may be expressed in non-ALGOL language. Since it is intended that the use of this feature should be entirely a question of the hardware representation, the Revised Report gives no further rules concerning this code language.

We have chosen to define Code as a body of text enclosed in braces, possibly with nested text of the same form. A C procedure body or compound statement takes this form, which is recognized by the Ctext auxiliary scanner:

Code as Procedure Body[121]==
Code:   $\{     (Ctext) [mkstr]
This macro is invoked in definition 4.
The { must be escaped because it is a regular expression operator.

The token processor mkstr assigns a unique integer value to each code body regardless of its content. This integer value can be used to access the code body.