Original PDF

HTML-source

The Programming Language Oberon

Revision 1.10.2013 / 3.5.2016

Niklaus Wirth

Make it as simple as possible, but not simpler. (A. Einstein)

Table of Contents

  1. History and introduction
  2. Syntax
  3. Vocabulary
  4. Declarations and scope rules
  5. Constant declarations
  6. Type declarations
    1. Basic types
    2. Array types
    3. Record types
    4. Pointer types
    5. Procedure types
  7. Variable declarations
  8. Expressions
    1. Operands
    2. Operators
  9. Statements
    1. Assignments
    2. Procedure calls
    3. Statement sequences
    4. If statements
    5. Case statements
    6. While statements
    7. Repeat Statements
    8. For statements
  10. Procedure declarations
    1. Formal parameters
    2. Predefined procedures
  11. Modules
    1. The Module SYSTEM
Appendix: The Syntax of Oberon

1. Introduction

Oberon is a general-purpose programming language that evolved from Modula-2. Its principal new feature is the concept of type extension. It permits the construction of new data types on the basis of existing ones and to relate them.

This report is not intended as a programmer's tutorial. It is intentionally kept concise. Its function is to serve as a reference for programmers, implementors, and manual writers. What remains unsaid is mostly left so intentionally, either because it is derivable from stated rules of the language, or because it would unnecessarily restrict the freedom of implementors.

This document describes the language defined in 1988/90 as revised in 2007 / 2016.

2. Syntax

A language is an infinite set of sentences, namely the sentences well formed according to its syntax. In Oberon, these sentences are called compilation units. Each unit is a finite sequence of symbols from a finite vocabulary. The vocabulary of Oberon consists of identifiers, numbers, strings, operators, delimiters, and comments. They are called lexical symbols and are composed of sequences of characters. (Note the distinction between symbols and characters.)

To describe the syntax, an extended Backus-Naur Formalism called EBNF is used. Brackets [ and ] denote optionality of the enclosed sentential form, and braces { and } denote its repetition (possibly 0 times). Syntactic entities (non-terminal symbols) are denoted by English words expressing their intuitive meaning. Symbols of the language vocabulary (terminal symbols) are denoted by strings enclosed in quote marks or by words in capital letters.

3. Vocabulary

The following lexical rules must be observed when composing symbols. Blanks and line breaks must not occur within symbols (except in comments, and blanks in strings). They are ignored unless they are essential to separate two consecutive symbols. Capital and lower-case letters are considered as being distinct.

Identifiers are sequences of letters and digits. The first character must be a letter.

ident  =  letter {letter | digit}.

Examples:

x  Scan  Oberon  GetSymbol  firstLetter

Numbers are (unsigned) integers or real numbers. Integers are sequences of digits and may be followed by a suffix letter. If no suffix is specified, the representation is decimal. The suffix H indicates hexadecimal representation.

A real number always contains a decimal point. Optionally it may also contain a decimal scale factor. The letter E is pronounced as "times ten to the power of".

number  =  integer | real.
integer  =  digit {digit} | digit {hexDigit} "H" .
real  =  digit {digit} "." {digit} [ScaleFactor].
ScaleFactor  =  "E" ["+" | "-"] digit {digit}.
hexDigit  =  digit | "A" | "B" | "C" | "D" | "E" | "F".
digit  =  "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".

Examples:

1987
100H     = 256
12.3
4.567E8  = 456700000

Strings are sequences of characters enclosed in quote marks ("). A string cannot contain the delimiting quote mark. Alternatively, a single-character string may be specified by the ordinal number of the character in hexadecimal notation followed by an "X". The number of characters in a string is called the length of the string.

string = """ {character} """ | digit {hexdigit} "X".

Examples:

"OBERON"    "Don't worry!"    22X 

Operators and delimiters — are the special characters, character pairs, or reserved words listed below. These reserved words consist exclusively of capital letters and cannot be used in the role of identifiers.

+ := ARRAYIMPORT THEN
- ^ BEGININ TO
* = BY IS TRUE
/ # CASE MOD TYPE
~ < CONSTMODULE UNTIL
&>DIV NIL VAR
. <=DO OF WHILE
, >=ELSE OR
; .. ELSIFPOINTER
| : END PROCEDURE
( ) FALSERECORD
[ ] FOR REPEAT
{ } IF RETURN

Comments may be inserted between any two symbols in a program. They are arbitrary character sequences opened by the bracket (* and closed by *). Comments do not affect the meaning of a program. They may be nested.

4. Declarations and scope rules

Every identifier occurring in a program must be introduced by a declaration, unless it is a predefined identifier. Declarations also serve to specify certain permanent properties of an object, such as whether it is a constant, a type, a variable, or a procedure.

The identifier is then used to refer to the associated object. This is possible in those parts of a program only which are within the scope of the declaration. No identifier may denote more than one object within a given scope. The scope extends textually from the point of the declaration to the end of the block (procedure or module) to which the declaration belongs and hence to which the object is local.

In its declaration, an identifier in the module's scope may be followed by an export mark (*) to indicate that it be exported from its declaring module. In this case, the identifier may be used in other modules, if they import the declaring module. The identifier is then prefixed by the identifier designating its module (see Ch. 11). The prefix and the identifier are separated by a period and together are called a qualified identifier.

qualident = [ident "."] ident.
identdef = ident ["*"].

The following identifiers are predefined; their meaning is defined in section 6.1 (types) или 10.2 (procedures):

ABS ASRASSERT BOOLEANBYTE
CHARCHRDEC EXCL FLOOR
FLT INCINCL INTEGERLEN
LSL NEWODD ORD PACK
REALRORSET UNPK

5. Constant declarations

A constant declaration associates an identifier with a constant value.

ConstDeclaration  =  identdef "=" ConstExpression.
ConstExpression   =  expression.

A constant expression can be evaluated by a mere textual scan without actually executing the program. Its operands are constants (see Ch. 8). Examples of constant declarations are:

N      = 100
limit  = 2*N-1
all    = {0 .. WordSize-1}
name   = "Oberon"

6. Type declarations

A data type determines the set of values which variables of that type may assume, and the operators that are applicable. A type declaration is used to associate an identifier with a type. The types define the structure of variables of this type and, by implication, the operators that are applicable to components. There are two different data structures, namely arrays and records, with different component selectors.

TypeDeclaration  =  identdef "=" type.
type = qualident | ArrayType | RecordType | PointerType | ProcedureType.

Examples:

Table      = ARRAY N OF REAL
Tree       = POINTER TO Node
Node       = RECORD key: INTEGER;
                left, right: Tree
             END
CenterNode = RECORD (Node)
                name: ARRAY 32 OF CHAR;
                subnode: Tree
             END
Function   = PROCEDURE (x: INTEGER): INTEGER

6.1 Basic types

The following basic types are denoted by predeclared identifiers. The associated operators are defined in 8.2, and the predeclared function procedures in 10.2. The values of a given basic type are the following:

BOOLEAN the truth values TRUE and FALSE
CHAR the characters of a standard character set
INTEGER the integers
REAL real numbers
BYTE the integers between 0 and 255
SET the sets of integers between 0 and an implementation-dependent limit

The type BYTE is compatible with the type INTEGER, and vice-versa.

6.2 Array types

An array is a structure consisting of a fixed number of elements which are all of the same type, called the element type. The number of elements of an array is called its length. The elements of the array are designated by indices, which are integers between 0 and the length minus 1.

ArrayType  =  ARRAY length {"," length} OF type.
length  =  ConstExpression.

A declaration of the form

ARRAY N0, N1, ..., Nk OF T

is understood as an abbreviation of the declaration

ARRAY N0 OF
     ARRAY N1 OF
       ...
           ARRAY Nk OF T

Examples of array types:

ARRAY N OF INTEGER
ARRAY 10, 20 OF REAL

6.3 Record types

A record type is a structure consisting of a fixed number of elements of possibly different types. The record type declaration specifies for each element, called field, its type and an identifier which denotes the field. The scope of these field identifiers is the record definition itself, but they are also visible within field designators (see 8.1) referring to elements of record variables.

RecordType   =  RECORD ["(" BaseType ")"] [FieldListSequence] END.
BaseType     =  qualident.
FieldListSequence  =  FieldList {";" FieldList}.
FieldList    =  IdentList ":" type.
IdentList    =  identdef {"," identdef}.

If a record type is exported, field identifiers that are to be visible outside the declaring module must be marked. They are called public fields; unmarked fields are called private fields.

Record types are extensible, i.e. a record type can be defined as an extension of another record type. In the examples above, CenterNode (directly) extends Node, which is the (direct) base type of CenterNode. More specifically, CenterNode extends Node with the fields name and subnode.

Definition: A type T extends a type T0, if it equals T0, or if it directly extends an extension of T0. Conversely, a type T0 is a base type of T, if it equals T, or if it is the direct base type of a base type of T.

Examples of record types:

RECORD day, month, year: INTEGER
END
RECORD
  name, firstname: ARRAY 32 OF CHAR;
  age: INTEGER;
  salary: REAL
END

6.4 Pointer types

Variables of a pointer type P assume as values pointers to variables of some type T. It must be a record type. The pointer type P is said to be bound to T, and T is the pointer base type of P. Pointer types inherit the extension relation of their base types, if there is any. If a type T is an extension of T0 and P is a pointer type bound to T, then P is also an extension of P0, the pointer type bound to T0.

PointerType  =  POINTER TO type.

If a type P is defined as POINTER TO T, the identifier T can be declared textually following the declaration of P, but [if so] it must lie within the same scope.

If p is a variable of type P = POINTER TO T, then a call of the predefined procedure NEW(p) has the following effect (see 10.2): A variable of type T is allocated in free storage, and a pointer to it is assigned to p. This pointer p is of type P and the referenced variable p^ is of type T. Failure of allocation results in p obtaining the value NIL. Any pointer variable may be assigned the value NIL, which points to no variable at all.

6.5 Procedure types

Variables of a procedure type T have a procedure (or NIL) as value. If a procedure P is assigned to a procedure variable of type T, the (types of the) formal parameters of P must be the same as those indicated in the formal parameters of T. The same holds for the result type in the case of a function procedure (see 10.1). P must not be declared local to another procedure, and neither can it be a standard procedure.

ProcedureType = PROCEDURE [FormalParameters].

7. Variable declarations

Variable declarations serve to introduce variables and associate them with identifiers that must be unique within the given scope. They also serve to associate fixed data types with the variables.

VariableDeclaration  =  IdentList ":" type.

Variables whose identifiers appear in the same list are all of the same type. Examples of variable declarations (refer to examples in Ch. 6):

i, j, k: INTEGER
x, y:    REAL
p, q:    BOOLEAN
s:       SET
f:       Function
a:       ARRAY 100 OF REAL
w:       ARRAY 16 OF
            RECORD ch: CHAR;
               count: INTEGER
            END
t:       Tree

8. Expressions

Expressions are constructs denoting rules of computation whereby constants and current values of variables are combined to derive other values by the application of operators and function procedures. Expressions consist of operands and operators. Parentheses may be used to express specific associations of operators and operands.

8.1 Operands

With the exception of sets and literal constants, i.e. numbers and strings, operands are denoted by designators. A designator consists of an identifier referring to the constant, variable, or procedure to be designated. This identifier may possibly be qualified by module identifiers (see Ch. 4 and 11), and it may be followed by selectors, if the designated object is an element of a structure.

If A designates an array, then A[E] denotes that element of A, whose index is the current value of the expression E. The type of E must be of type INTEGER. A designator of the form A[E1, E2, ..., En] stands for A[E1][E2]...[En]. If p designates a pointer variable, p^ denotes the variable which is referenced by p. If r designates a record, then r.f denotes the field f of r. If p designates a pointer, p.f denotes the field f of the record p^, i.e. the dot implies dereferencing and p.f stands for p^.f.

The typeguard v(Т0) asserts that v is of type T0, i.e. it aborts program execution, if it is not of type T0. The guard is applicable, if

  1. T0 is an extension of the declared type T of v, and if
  2. v is a variable parameter of record type, or v is a pointer.
designator  =  qualident {selector}.
selector  =  "." ident | "[" ExpList "]" | "^" | "(" qualident ")".
ExpList  =  expression {"," expression}.

If the designated object is a variable, then the designator refers to the variable's current value. If the object is a procedure, a designator without parameter list refers to that procedure. If it is followed by a (possibly empty) parameter list, the designator implies an activation of the procedure and stands for the value resulting from its execution. The (types of the) actual parameters must correspond to the formal parameters as specified in the procedure's declaration (see Ch. 10).

Examples of designators (see examples in Ch. 7):

i (INTEGER)
a[i] (REAL)
w[3].ch (CHAR)
t.key (INTEGER)
t.left.right (Tree)
t(CenterNode).subnode(Tree)

8.2 Operators

The syntax of expressions distinguishes between four classes of operators with different precedences (binding strengths). The operator ~ has the highest precedence, followed by multiplication operators, addition operators, and relations. Operators of the same precedence associate from left to right. For example, x-y-z stands for (x-y)-z.

expression   = SimpleExpression [[relation SimpleExpression].
relation     = "=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS.
SimpleExpression  = ["+"|"-"] term {AddOperator term}.
AddOperator  = "+" | "-" | OR.
term         = factor {MulOperator factor}.
MulOperator  = "*" | "/" | DIV | MOD | "&" .
factor       = number | string | NIL | TRUE | FALSE | set |
      designator [ActualParameters] | "(" expression ")" | "~" factor.
set          =  "{" [element {"," element}] "}".
element      = expression [".." expression].
ActualParameters = "(" [ExpList] ")".

The set {m .. n} denotes {m, m+1, ... , n-1, n}, and if m > n, the empty set. The available operators are listed in the following tables. In some instances, several different operations are designated by the same operator symbol. In these cases, the actual operation is identified by the type of the operands.

8.2.1 Logical operators

symbolresult
OR logical disjunction
& logical conjunction
~ negation

These operators apply to BOOLEAN operands and yield a BOOLEAN result.

р OR q stands for"if p then TRUE, else q"
p & q stands for"if p then q, else FALSE"
~ p stands for"not p"

8.2.2 Arithmetic operators

symbolrelation
+ sum
- difference
* product
/ quotient
DIV integer quotient
MOD modulus

The operators +, -, *, and / apply to operands of numeric types. Both operands must be of the same type, which is also the type of the result. When used as unary operators, - denotes sign inversion and + denotes the identity operation.

The operators DIV and MOD apply to integer operands only. Let q = x DIV y, and r = x MOD y. Then quotient q and remainder r are defined by the equation

X = q*y + r         0 <= r < y

8.2.3 Set operators

symbolresult
+ union
- difference
* intersection
/ symmetric set difference

When used with a single operand of type SET, the minus sign denotes the set complement.

8.2.4 Relations

symbolresult
= equal
# unequal
< less
<= less or equal
> greater
>= greater or equal
IN set membership
IS type test

Relations are Boolean. The ordering relations <, <=, >, >= apply to the numeric types, CHAR, and character arrays. The relations = and # also apply to the types BOOLEAN, SET, and to pointer and procedure types.

x IN s stands for "x is an element of s". x must be of type INTEGER, and s of type SET.

v IS T stands for "v is of type T" and is called type test. It is applicable, if

  1. T is an extension of the declared type T0 of v, and if
  2. v is a variable parameter of record type or v is a pointer.

Assuming, for instance, that T is an extension of T0 and that v is a designator declared of type T0, then the test v IS T determines whether the actually designated variable is (not only a T0, but also) a T. The value of NIL IS T is undefined.

Examples of expressions (refer to examples in Ch. 7):

1987 (INTEGER)
i DIV 3 (INTEGER)
~ p OR q (BOOLEAN)
(I + j) * (i-j) (INTEGER)
s - {8, 9, 13} (SET)
a[i+j] * a [i-j] (REAL)
(0 <= i) & (i < 100)(BOOLEAN)
t.key = 0 (BOOLEAN)
K IN {i .. j-1} (BOOLEAN)
T IS CenterNode (BOOLEAN)

9. Statements

Statements denote actions. There are elementary and structured statements. Elementary statements are not composed of any parts that are themselves statements. They are the assignment and the procedure call. Structured statements are composed of parts that are themselves statements. They are used to express sequencing and conditional, selective, and repetitive execution. A statement may also be empty, in which case it denotes no action. The empty statement is included in order to relax punctuation rules in statement sequences.

statement = [assignment | ProcedureCall | IfStatement |
   CaseStatement | WhileStatement | RepeatStatement | ForStatement].

9.1 Assignments

The assignment serves to replace the current value of a variable by a new value specified by an expression. The assignment operator is written as ":=" and pronounced as becomes.

assignment = designator ":=" expression.

If a value parameter is structured (of array or record type), no assignment to it or to its elements are permitted. Neither may assignments be made to imported variables.

The type of the expression must be the same as that of the designator. The following exceptions hold:

  1. The constant NIL can be assigned to variables of any pointer or procedure type.
  2. Strings can be assigned to any array of characters, provided the number of characters in the string is less than that of the array. (A null character is appended). Single-character strings can also be assigned to variables of type CHAR.
  3. In the case of records, the type of the source must be an extension of the type of the destination.
  4. An open array may be assigned to an array of equal base type.

Examples of assignments (see examples in Ch. 7):

i := 0
p := i = j
x := FLT(i + 1)
k := (i + j) DIV 2
f := log2
s := {2, 3, 5, 7, 11, 13}
a[i] := (x+y) * (x-y)
t.key := i
w[i+1].ch := "A"

9.2 Procedure calls

A procedure call serves to activate a procedure. The procedure call may contain a list of actual parameters which are substituted in place of their corresponding formal parameters defined in the procedure declaration (see Ch. 10). The correspondence is established by the positions of the parameters in the lists of actual and formal parameters respectively. There exist two kinds of parameters: variable and value parameters.

In the case of variable parameters, the actual parameter must be a designator denoting a variable. If it designates an element of a structured variable, the selector is evaluated when the formal/actual parameter substitution takes place, i.e. before the execution of the procedure. If the parameter is a value parameter, the corresponding actual parameter must be an expression. This expression is evaluated prior to the procedure activation, and the resulting value is assigned to the formal parameter which now constitutes a local variable (see also 10.1).

ProcedureCall = designator [ActualParameters].
Examples of procedure calls:
ReadInt(i)         (see Ch. 10)
WriteInt(2*j + 1, 6)
INC(w[k].count)

9.3 Statement sequences

Statement sequences denote the sequence of actions specified by the component statements which are separated by semicolons.

StatementSequence = statement {";" statement}.

9.4 If statements

IfStatement = IF expression THEN StatementSequence
   {ELSIF expression THEN StatementSequence}
   [ELSE StatementSequence]
   END.

If statements specify the conditional execution of guarded statements. The Boolean expression preceding a statement is called its guard. The guards are evaluated in sequence of occurrence, until one evaluates to TRUE, whereafter its associated statement sequence is executed. If no guard is satisfied, the statement sequence following the symbol ELSE is executed, if there is one.

Example:

IF (ch >= "A") & (ch <= "Z") THEN ReadIdentifier
ELSIF (ch >= "0") & (ch <= "9") THEN ReadNumber
ELSIF ch = 22X THEN ReadString
END

9.5 Case statements

Case statements specify the selection and execution of a statement sequence according to the value of an expression. First the case expression is evaluated, then the statement sequence is executed whose case label list contains the obtained value. If the case expression is of type INTEGER or CHAR, all labels must be integers or single-character strings, respectively.

CaseStatement = CASE expression OF case {"|" case} END.
case = [CaseLabelList ":" StatementSequence].
CaseLabelList = LabelRange {"," LabelRange }.
LabelRange = label [".." label].
label = integer | integer| qualident.

Example:

CASE k OF
    0: x := x + y
  | 1: x := x − y
  | 2: x := x * y
  | 3: x := x / y
END

The type T of the case expression (case variable) may also be a record or pointer type. Then the case labels must be extensions of T, and in the statements Si labelled by Ti, the case variable is considered as of type Ti.

Example:

TYPE R = RECORD a: INTEGER END ;
   R0 = RECORD (R) b: INTEGER END ;
   R1 = RECORD (R) b: REAL END ;
   R2 = RECORD (R) b: SET END ;
   P = POINTER TO R;
   P0 = POINTER TO R0;
   P1 = POINTER TO R1;
   P2 = POINTER TO R2;
VAR p: P;
CASE p OF
   P0: p.b := 10 |
   P1: p.b := 2.5 |
   P2: p.b := {0, 2}
END

9.6 While statements

While statements specify repetition. If any of the Boolean expressions (guards) yields TRUE, the corresponding statement sequence is executed. The expression evaluation and the statement execution are repeated until none of the Boolean expressions yields TRUE.

WhileStatement = WHILE expression DO StatementSequence
   {ELSIF expression DO StatementSequence} END.

Examples:

WHILE j > 0 DO
   j := j DIV 2; i := i+1
END
WHILE (t # NIL) & (t.key # i) DO
   t := t.left
END
WHILE m > n DO m := m - n
ELSIF n > m DO n := n - m
END

9.7 Repeat Statements

A repeat statement specifies the repeated execution of a statement sequence until a condition is satisfied. The statement sequence is executed at least once.

RepeatStatement = REPEAT StatementSequence UNTIL expression.

9.8 For statements

A for statement specifies the repeated execution of a statement sequence for a given number of times, while a progression of values is assigned to an integer variable called the control variable of the for statement.

ForStatement =
  FOR ident ":=" expression TO expression [BY ConstExpression] DO
  StatementSequence END.

The for statement

FOR v := beg TO end BY inc DO S END

is, if inc > 0, equivalent to

v := beg;
WHILE v <= end DO S; v := v + inc END

and if inc < 0 it is equivalent to

v := beg;
WHILE v >= end DO S; v := v + inc END

The types v, beg and end must be INTEGER, and inc must be an integer (constant expression). If the step is not specified, it is assumed to be 1.

10. Procedure declarations

Procedure declarations consist of a procedure heading and a procedure body. The heading specifies the procedure identifier, the formal parameters, and the result type (if any). The body contains declarations and statements. The procedure identifier is repeated at the end of the procedure declaration.

There are two kinds of procedures, namely proper procedures and function procedures. The latter are activated by a function designator as a constituent of an expression, and yield a result that is an operand in the expression. Proper procedures are activated by a procedure call. A function procedure is distinguished in the declaration by indication of the type of its result following the parameter list. Its body must end with a RETURN clause which defines the result of the function procedure.

All constants, variables, types, and procedures declared within a procedure body are local to the procedure. The values of local variables are undefined upon entry to the procedure. Since procedures may be declared as local objects too, procedure declarations may be nested.

In addition to its formal parameters and locally declared objects, the objects declared globally are also visible in the procedure.

The use of the procedure identifier in a call within its declaration implies recursive activation of the procedure.

ProcedureDeclaration = ProcedureHeading ";" ProcedureBody ident.
ProcedureHeading     = PROCEDURE identdef [FormalParameters].
ProcedureBody        = DeclarationSequence [BEGIN StatementSequence]
       [RETURN expression] END.
DeclarationSequence  = [CONST {ConstDeclaration ";"}]
       [TYPE {TypeDeclaration ";"}] [VAR {VariableDeclaration ";"}]
       {ProcedureDeclaration ";"}.

10.1 Formal parameters

Formal parameters are identifiers which denote actual parameters specified in the procedure call. The correspondence between formal and actual parameters is established when the procedure is called. There are two kinds of parameters, namely value and variable parameters. A variable parameter corresponds to an actual parameter that is a variable, and it stands for that variable. A value parameter corresponds to an actual parameter that is an expression, and it stands for its value, which cannot be changed by assignment. However, if a value parameter is of a basic type, it represents a local variable to which the value of the actual expression is initially assigned.

The kind of a parameter is indicated in the formal parameter list: Variable parameters are denoted by the symbol VAR and value parameters by the absence of a prefix.

A function procedure without parameters must have an empty parameter list. It must be called by a function designator whose actual parameter list is empty too.

Formal parameters are local to the procedure, i.e. their scope is the program text which constitutes the procedure declaration.

FormalParameters = "(" [FPSection {";" FPSection}] ")" [":" qualident].
FPSection = [VAR] ident {"," ident} ":" FormalType.
FormalType = {ARRAY OF} qualident.

The type of each formal parameter is specified in the parameter list. For variable parameters, it must be identical to the corresponding actual parameter's type, except in the case of a record, where it must be a base type of the corresponding actual parameter's type.

If the formal parameter's type is specified as

ARRAY OF T

the parameter is said to be an open array, and the corresponding actual parameter may be of arbitrary length.

If a formal parameter specifies a procedure type, then the corresponding actual parameter must be either a procedure declared globally, or a variable (or parameter) of that procedure type. It cannot be a predefined procedure. The result type of a procedure can be neither a record nor an array.

Examples of procedure declarations:

PROCEDURE ReadInt(VAR x: INTEGER);
   VAR i : INTEGER; ch: CHAR;
BEGIN i := 0; Read(ch);
   WHILE ("0" <= ch) & (ch <= "9") DO
      i := 10*i + (ORD(ch)-ORD("0")); Read(ch)
   END ;
   x := i
END ReadInt
PROCEDURE WriteInt(x: INTEGER); (* 0 <= x < 10^5 *)
   VAR i: INTEGER;
       buf: ARRAY 5 OF INTEGER;
BEGIN i := 0;
   REPEAT buf[i] := x MOD 10; x := x DIV 10; INC(i) UNTIL x = 0;
   REPEAT DEC(i); Write(CHR(buf[i] + ORD("0"))) UNTIL i = 0
END WriteInt
PROCEDURE log2(x: INTEGER): INTEGER;
   VAR y: INTEGER; (*assume x>0*)
BEGIN y := 0;
   WHILE x > 1 DO x := x DIV 2; INC(y) END ;
   RETURN y
END log2

10.2 Predefined procedures

The following table lists the predefined procedures. Some are generic procedures, i.e. they apply to several types of operands. v stands for a variable, x and n for expressions, and T for a type.

Function procedures:

NameArgument typesResult typeFunction
ABS(x)x: numeric typetype of xabsolute value
ODD(x)x: INTEGERBOOLEANx MOD 2 = 1
LEN(v)v: arrayINTEGERthe length of v
LSL(x, n)x, n: INTEGERINTEGERlogical shift left, x * 2n
ASR(x, n)x, n: INTEGERINTEGERsigned shift right, x DIV 2n
ROR(x, n)x, n: INTEGERINTEGERx rotated right by n bits

Type conversion functions:

NameArgument typesResult typeFunction
FLOOR(x)REALINTEGERround down
FLT(x)INTEGERREALidentity
ORD(x)CHAR, BOOLEAN, SETINTEGERordinal number of x
CHR(x)INTEGERCHARcharacter with ordinal number x

Proper procedures:

Name Argument types Function
INC(v) INTEGER v := v + 1
INC(v, n) INTEGER v := v + n
DEC(v) INTEGER v := v - 1
DEC(v, n) INTEGER v := v - n
INCL(v, x)v: SET; x: INTEGERv := v + {x}
EXCL(v, x)v: SET; x: INTEGERv := v - {x}
NEW(v) pointer type allocate v^
ASSERT(b) BOOLEAN abort, if ~b
PACK(x, n)REAL; INTEGER pack x and n into x
UNPK(x, n)REAL; INTEGER unpack x into x and n

The function FLOOR(x) yields the largest integer not greater than x.

FLOOR(1.5) = 1     FLOOR(-1.5) = -2

The parameter n of PACK represents the exponent of x. PACK(x, y) is equivalent to x := x * 2y. UNPK is the reverse operation. The resulting x is normalized, such that 1.0 <= x < 2.0.

11. Modules

A module is a collection of declarations of constants, types, variables, and procedures, and a sequence of statements for the purpose of assigning initial values to the variables. A module typically constitutes a text that is compilable as a unit.

module      = МODULE ident ";" [[ImportList] DeclarationSequence
            [BEGIN StatementSequence] END ident ".".
ImportList  = IMPORT import {"," import} ";".
Import      = [ident ":="] ident.

The import list specifies the modules of which the module is a client. If an identifier x is exported from a module M, and if M is listed in a module's import list, then x is referred to as M.x. If the form "M := M1" is used in the import list, an exported object x declared within M1 is referenced in the importing module as M.x .

Identifiers that are to be visible in client modules, i.e. which are to be exported, must be marked by an asterisk (export mark) in their declaration. Variables are always exported in read-only mode.

The statement sequence following the symbol BEGIN is executed when the module is added to a system (loaded). Individual (parameterless) procedures can thereafter be activated from the system, and these procedures serve as commands.

Example:

MODULE Out;    (*exported procedures: Write, WriteInt, WriteLn*)
  IMPORT Texts, Oberon;
  VAR W: Texts.Writer;
  PROCEDURE Write*(ch: CHAR);
  BEGIN Texts.Write(W, ch)
  END ;
  PROCEDURE WriteInt*(x, n: INTEGER);
    VAR i: INTEGER; a: ARRAY 16 OF CHAR;
  BEGIN i := 0;
    IF x < 0 THEN Texts.Write(W, "-"); x := -x END ;
    REPEAT a[i] := CHR(x MOD 10 + ORD("0")); x := x DIV 10; INC(i) UNTIL x = 0;
    REPEAT Texts.Write(W, " "); DEC(n) UNTIL n <= i;
    REPEAT DEC(i); Texts.Write(W, a[i]) UNTIL i = 0
  END WriteInt;
  PROCEDURE WriteLn*;
  BEGIN Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf)
  END WriteLn;
BEGIN Texts.OpenWriter(W)
END Out.

11.1 The Module SYSTEM

The optional module SYSTEM contains definitions that are necessary to program low-level operations referring directly to resources particular to a given computer and/or implementation.

These include for example facilities for accessing devices that are controlled by the computer, and perhaps facilities to break the data type compatibility rules otherwise imposed by the language definition.

There are two reasons for providing facilites in Module SYSTEM ; (1) Their value is implementation-dependent, that is, it is not derivable from the language's definition, and (2) they may corrupt a system (e.g. PUT). It is strongly recommended to restrict their use to specific low-level modules, as such modules are inherently non-portable and not "type-safe". However, they are easily recognized due to the identifier SYSTEM appearing in the module's import lists. The subsequent definitions are generally applicable. However, individual implementations may include in their module SYSTEM additional definitions that are particular to the specific, underlying computer. In the following, v stands for a variable, x, a and n for expressions.

Function procedures:

Name Argument types Result typeFunction
ADR(v) any INTEGER address of variable v
SIZE(T) any type INTEGER size in bytes
BIT(a, n) a, n: INTEGER BOOLEAN bit n of mem[a]

Proper procedures:

Name Argument types Function
GET(a, v) a: INTEGER; v: any basic typev := mem[a]
PUT(a, x) a: INTEGER; x: any basic typemem[a] := x
COPY(src, dst, n) all INTEGER copy n consecutive words from src to dst

The following are additional procedures accepted by the compiler for the RISC processor:

Function procedures:

Name Argument types Result typeFunction
VAL(T, n) scalar T identity
ADC(m, n) INTEGER INTEGER add with carry C
SBC(m, n) INTEGER INTEGER subtract with carry C
UML(m, n) INTEGER INTEGER unsigned multiplication
COND(n) INTEGER BOOLEAN IF Cond(n) THEN ...

Proper procedures:

Name Argument types Function
LED(n)INTEGER display n on LEDs

Appendix

The Syntax of Oberon

letter = "A" | "B" | ... | "Z" | "a" | "b" | ... | "z".
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".
hexDigit = digit | "A" | "B" | "C" | "D" | "E" | "F".
ident = letter {letter | digit}.
qualident = [ident "."] ident.
identdef = ident ["*"].
integer = digit {digit} | digit {hexDigit} "H".
real = digit {digit} "." {digit} [ScaleFactor].
ScaleFactor = "E" ["+" | "-"] digit {digit}.
number = integer | real.
string = """ {character} """ | digit {hexDigit} "X".
ConstDeclaration = identdef "=" ConstExpression.
ConstExpression = expression.
TypeDeclaration = identdef "=" type.
type = qualident | ArrayType | RecordType | PointerType | ProcedureType.
ArrayType = ARRAY length {"," length} OF type.
length = ConstExpression.
RecordType = RECORD ["(" BaseType ")"] [FieldListSequence] END.
BaseType = qualident.
FieldListSequence = FieldList {";" FieldList}.
FieldList = IdentList ":" type.
IdentList = identdef {"," identdef}.
PointerType = POINTER TO type.
ProcedureType = PROCEDURE [FormalParameters].
VariableDeclaration = IdentList ":" type.
expression = SimpleExpression [relation SimpleExpression].
relation = "=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS.
SimpleExpression = ["+" | "-"] term {AddOperator term}.
AddOperator = "+" | "-" | OR.
term = factor {MulOperator factor}.
MulOperator = "*" | "/" | DIV | MOD | "&".
factor = number | string | NIL | TRUE | FALSE |
   set | designator [ActualParameters] | "(" expression ")" | "~" factor.
designator = qualident {selector}.
selector = "." ident | "[" ExpList "]" | "^" | "(" qualident ")".
set = "{" [element {"," element}] "}".
element = expression [".." expression].
ExpList = expression {"," expression}.
ActualParameters = "(" [ExpList] ")" .
statement = [assignment | ProcedureCall | IfStatement | CaseStatement |
WhileStatement | RepeatStatement | ForStatement].
assignment = designator ":=" expression.
ProcedureCall = designator [ActualParameters].
StatementSequence = statement {";" statement}.
IfStatement = IF expression THEN StatementSequence
   {ELSIF expression THEN StatementSequence}
   [ELSE StatementSequence] END.
CaseStatement = CASE expression OF case {"|" case} END.
case = [CaseLabelList ":" StatementSequence].
CaseLabelList = LabelRange {"," LabelRange}.
LabelRange = label [".." label].
label = integer | string | qualident.
WhileStatement = WHILE expression DO StatementSequence
   {ELSIF expression DO StatementSequence} END.
RepeatStatement = REPEAT StatementSequence UNTIL expression.
ForStatement = FOR ident ":=" expression TO expression [BY ConstExpression]
   DO StatementSequence END.
ProcedureDeclaration = ProcedureHeading ";" ProcedureBody ident.
ProcedureHeading = PROCEDURE identdef [FormalParameters].
ProcedureBody = DeclarationSequence [BEGIN StatementSequence]
   [RETURN expression] END.
DeclarationSequence = [CONST {ConstDeclaration ";"}]
   [TYPE {TypeDeclaration ";"}]
   [VAR {VariableDeclaration ";"}]
   {ProcedureDeclaration ";"}.
FormalParameters = "(" [FPSection {";" FPSection}] ")" [":" qualident].
FPSection = [VAR] ident {"," ident} ":" FormalType.
FormalType = {ARRAY OF} qualident.
module = MODULE ident ";" [ImportList] DeclarationSequence
   [BEGIN StatementSequence] END ident "." .
ImportList = IMPORT import {"," import} ";".
import = ident [":=" ident].

Изначально переведено Иваном Денисовым c использованием переводов:

Замечания по переводу: Александр Ширяев, Артур Ефимов, Александр Легалов и другие.
Отредактировано comdivbyzero.

PDF-первоисточник

Исходный код HTML

Язык программирования Оберон

Ревизия 1.10.2013 / 3.5.2016

Никлаус Вирт

Сделай настолько просто, насколько возможно, но не проще. (А. Эйнштейн)

Оглавление

  1. История и введение
  2. Синтаксис
  3. Словарь
  4. Описания и правила области видимости
  5. Описания констант
  6. Описания типов
    1. Базовые типы
    2. Типы массив
    3. Типы запись
    4. Типы указатель
    5. Процедурный тип
  7. Описания переменных
  8. Выражения
    1. Операнды
    2. Операции
  9. Операторы
    1. Присваивания
    2. Вызовы процедур
    3. Последовательность операторов
    4. Операторы If
    5. Операторы Case
    6. Операторы While
    7. Операторы Repeat
    8. Операторы For
  10. Описания процедур
    1. Формальные параметры
    2. Предопределенные процедуры-функции
  11. Модули
    1. Модуль SYSTEM
Приложение: Синтаксис Оберона

1. История и введение

Оберон — язык программирования общего назначения, эволюционировавший из Модула-2. Его принципиально новое свойство — понятие расширения типа. Оно позволяет создавать новые типы данных на основе существующих и соотносить их между собой.

Этот документ не учебник для программистов. Он намеренно краток. Его назначение — служить справкой для программистов, разработчиков воплощений и авторов руководств. Недосказанное, преимущественно, оставлено так умышленно: или потому, что это следует из других правил языка, или потому, что без необходимости может ограничить свободу воплотителей.

Этот документ описывает язык, определённый в 1988/90 и пересмотренный в 2007/2016.

2. Синтаксис

Язык — это бесконечное множество предложений, правильно оформленных в соответствии с его синтаксисом. В Обероне эти предложения называются единицами компиляции. Каждая единица представляет собой конечную последовательность из конечного словаря. Словарь Оберона состоит из идентификаторов, чисел, строк, операций, разделителей и комментариев, которые называются лексемами и состоят из последовательностей литер.

Для описания синтаксиса использована расширенная Бэкус — Наурова форма (РБНФ). Скобки [ и ] означают необязательность заключённого выражения, а { и } — его повторение (от 0 раз). Синтаксические сущности (нетерминальные лексемы) обозначены словами, выражающими их интуитивный смысл. Лексемы словаря языка (терминальные лексемы) представлены строками в кавычках и словами из заглавных букв.

3. Словарь

При составлении лексем должны соблюдаться следующие правила. Пробелы и переносы не должны встречаться внутри лексем (но не в комментариях и не пробелы в строках). Они игнорируются, не считая значения в разделении двух последовательных лексем. Заглавные и строчные буквы считаются различными.

Идентификаторы — это последовательности букв и цифр. Первая литера должна быть буквой.

идент = буква {буква | цифра}.

Примеры:

x  Scan  Oberon  GetSymbol  firstLetter

Числа — это (беззнаковые) целые и дроби. Целые — это цепочки цифр, за которыми может следовать буква суффикса. Если суффикса нет, то представление десятичное. Суффикс H указывает на шестнадцатеричное представление.

Дробь всегда содержит десятичную точку. Опционально она также может содержать десятичный порядок. Буква E означает «умножить на десять в степени».

число       =  целое | дробь.
целое       =  цифра {цифра} | цифра {шестнЦифра} "H".
дробь       =  цифра {цифра} "." {цифра} [порядок].
порядок     =  ("E") ["+" | "-"] цифра {цифра}.
шестнЦифра  =  цифра | "A" | "B" | "C" | "D" | "E" | "F".
цифра       =  "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".

Примеры:

1987
100H     = 256
12.3
4.567E8  = 456700000

Строки — это цепочки литер, заключенные в двойные кавычки ("). Строка не может содержать разделяющую кавычку. По-другому, строка из одной литеры может быть задана порядковым номером литеры в шестнадцатеричной записи, завершаемой литерой "X". Число литер в строке называется длиной строки.

строка = """ {литера} """ | цифра {шестнЦифра} "X".

Примеры:

"OBERON"    "Don"t worry!"    22X

Операции и разделители — это особые литеры, пары литер или зарезервированные слова, перечисленные ниже. Зарезервированные слова состоят только из заглавных букв и не могут применяться в качестве идентификаторов.

+ := ARRAYIMPORT THEN
- ^ BEGININ TO
* = BY IS TRUE
/ # CASE MOD TYPE
~ < CONSTMODULE UNTIL
&>DIV NIL VAR
. <=DO OF WHILE
, >=ELSE OR
; .. ELSIFPOINTER
| : END PROCEDURE
( ) FALSERECORD
[ ] FOR REPEAT
{ } IF RETURN

Комментарии могут быть вставлены между любыми двумя лексемами в программе. Это произвольные последовательности литер, открываемые скобкой (* и закрываемые с *). Комментарии не влияют на значение программы. Могут быть вложенными.

4. Описания и правила области видимости

Каждый встречающийся в программе идентификатор, кроме предопределённых, должен быть введён описанием. Описание также служит для определения постоянных свойств объекта, например, является ли он константой, типом, переменной или процедурой.

Позже идентификатор используется для ссылки на связанный объект. Это возможно только в частях программы, находящихся в области видимости описания. Идентификатор не может обозначать больше чем один объект внутри данной области. Область простирается текстуально от точки описания до конца блока (процедуры или модуля), которому описание принадлежит, и в отношении которого объект является локальным.

За идентификатором, объявленным в блоке модуля, может следовать метка экспорта (*), означающая, что он экспортирован из описывающего его модуля. В этом случае идентификатор может быть использован в других модулях, если они импортируют объявляющий модуль. Тогда идентификатор предваряется идентификатором, обозначающим его модуль (см. Гл. 11). Их разделяет точка, и вместе они называются квалифицированным идентификатором.

весьИдент  =  [идент "."] идент.
идентОпр   =  идент ["*"].

Следующие идентификаторы являются предопределёнными; их значение описано в разделе 6.1 (типы) или 10.2 (процедуры):

ABS ASRASSERT BOOLEANBYTE
CHARCHRDEC EXCL FLOOR
FLT INCINCL INTEGERLEN
LSL NEWODD ORD PACK
REALRORSET UNPK

5. Описания констант

Описание константы связывает её значение с идентификатором.

ОписаниеКонстанты = идентОпр "=" КонстВыражение.
КонстВыражение    = выражение.

Константное выражение может быть вычислено при сканировании текста программы без ее выполнения. Его операнды - константы (см. Гл. 8). Примеры описания констант:

N      = 100
limit  = 2*N-1
all    = {0 .. WordSize-1}
name   = "Oberon"

6. Описания типов

Тип данных определяет множество значений, которые могут принимать переменные этого типа, и применимые операций. Описание типа используется для связи идентификатора с типом. Типы определяют структуру переменных этого типа и, как следствие, операции, которые применимы к компонентам. Существуют две разные структуры данных, а именно массивы и записи, с различными способами обращения к их компонентам (с различными селекторами).

ОписаниеТипа = идентОпр "=" тип.
тип = весьИдент | ТипМассив | ТипЗапись | ТипУказатель | ТипПроцедура.

Примеры:

Table      = ARRAY N OF REAL
Tree       = POINTER TO Node
Node       = RECORD key: INTEGER;
                left, right: Tree
             END
CenterNode = RECORD (Node)
                name: ARRAY 32 OF CHAR;
                subnode: Tree
             END
Function   = PROCEDURE (x: INTEGER): INTEGER

6.1 Базовые типы

Следующие базовые типы обозначаются предопределенными идентификаторами. Соответствующие операции определены в 8.2, а предопределенные процедуры-функции — в 10.2. Значения базовые типов таковы:

BOOLEAN логические значения TRUE и FALSE
CHAR литеры стандартного набора литер
INTEGER целые числа
REAL действительные числа
BYTE целые числа от 0 до 255
SET набор целых чисел между 0 и пределом, зависящим от реализации

Тип BYTE совместим с типом INTEGER, и наоборот.

6.2 Типы массив

Массив — это структура, состоящая из фиксированного числа элементов одинакового типа, называемого типом элементов. Количество элементов массива называется его длиной. Элементы массива обозначаются индексами, которые являются целыми числами от 0 до длины массива минус 1.

ТипМассив = ARRAY длина {"," длина} OF тип.
длина = КонстВыражение.

Описание вида

ARRAY N0, N1, ..., Nk OF T

понимается как сокращение описания

ARRAY N0 OF
     ARRAY N1 OF
       ...
           ARRAY Nk OF T

Примеры типа массив:

ARRAY N OF INTEGER
ARRAY 10, 20 OF REAL

6.3 Типы запись

Запись — это структура, состоящая из фиксированного числа элементов, которые могут иметь различные типы. Описание типа запись задает для каждого элемента, называемого полем, его тип и идентификатор, который обозначает это поле. Область видимости идентификаторов полей — само определение записи, но они также доступны через обозначения поля (см. 8.1), указывающее на элементы переменных типа запись.

ТипЗапись = RECORD ['(' БазовыйТип ')'] [ПоследСпискаПолей] END.
БазовыйТип = весьИдент.
ПоследСпискаПолей = СписокПолей {';' СписокПолей}.
СписокПолей = СписокИдент ':' тип.
СписокИдент = идентОпр {',' идентОпр}.

Если тип запись экспортируется, идентификаторы полей, которые должны быть видимыми вне модуля описания, должны быть помечены. Они называются общедоступными полями; не отмеченные поля называются приватными полями.

Записи являются расширяемыми, т.е. запись может быть определена как расширение другой записи. В приведенных выше примерах CenterNode (напрямую) расширяет Node, который является его (прямым) базовым типом CenterNode. А именно, CenterNode расширяет Node полями name и subnode.

Определение: тип T расширяет тип T0, если он и есть T0, или если он непосредственно расширяет расширение T0. И наоборот, тип T0 является базовым типом для типа T, если он и есть T, или если он является прямым базовым типом базового типа T.

Примеры типов запись:

RECORD day, month, year: INTEGER
END
RECORD
  name, firstname: ARRAY 32 OF CHAR;
  age: INTEGER;
  salary: REAL
END

6.4 Типы указатель

Переменные типа указатель P принимают в качестве значений указатели на переменные некоторого типа T. Этот тип должен быть записью. Тип указатель P называется связанным с типом T, а T — является базовым типом указателя P. Типы указатель наследуют отношение расширения базовых типов, если они есть. Если тип T является расширением T0 и P является указателем, связанным с T, то тогда P также является расширением P0, который является указателем, связанным с T0.

ТипУказатель = POINTER TO тип.

Если тип P определён как POINTER TO T, идентификатор T может быть текстуально описан после описания P, но [если это так] он должен находиться в пределах одной области.

Если p — переменная типа P = POINTER TO T, то вызов предопределённой процедуры NEW(p) имеет следующий эффект (см. 10.2): в свободной памяти выделяется место для переменной типа T, и указатель на нее присваивается к p. Этот указатель p типа P ссылается на переменную p^ типа T. Ошибка выделения памяти под структуру приводит к тому, что p присваивается значение NIL. Каждой переменной типа указатель может быть присвоено значение NIL, которое не указывает ни на какую переменную вообще.

6.5 Процедурные типы

Переменные процедурного типа T принимают в качестве значения процедуру или NIL. Если процедура P связана с переменной процедурного типа T, то типы формальных параметров процедуры P должны совпадать с типами соответствующих формальных параметров типа T. То же самое справедливо для типа результата в случае процедур-функций (см. 10.1). P не может быть локальной в другой процедуре, и также не может быть стандартной процедурой.

ТипПроцедура = PROCEDURE [ФормальныеПараметры].

7. Описания переменных

Описания переменных служат для введения переменных и связывания их с идентификаторами, которые должны быть уникальными в данной области видимости. Они также служат для связывания фиксированных типов данных с переменными.

ОписаниеПеременных = СписокИдент ':' тип.

Переменные, чьи идентификаторы находятся в одном списке, все одного типа. Примеры описания переменных (см. примеры в Гл. 6):

i, j, k: INTEGER
x, y:    REAL
p, q:    BOOLEAN
s:       SET
f:       Function
a:       ARRAY 100 OF REAL
w:       ARRAY 16 OF
            RECORD ch: CHAR;
               count: INTEGER
            END
t:       Tree

8. Выражения

Выражения — это конструкции, обозначающие правила вычислений, в которых константы и текущие значения переменных комбинируются для получения других значений применением операций и процедур-функций. Выражения состоят из операндов и операций. Круглые скобки могут применяться для специфического связывания операций с операндами.

8.1 Операнды

За исключением множеств и буквальных констант, т.е. чисел и строк, операнды задаются обозначениями. Обозначение может быть идентификатором константы, переменной или процедуры. Этот идентификатор может быть квалифицирован идентификатором модуля (см. Гл. 4 и 11) и может сопровождаться селекторами, если обозначенный объект — часть структуры.

Если A обозначает массив, то A[E] обозначает тот элемент A, индекс которого является текущим значением выражения E. Результат E должен иметь тип INTEGER. Обозначение вида A[E1, E2, ..., En] значит A[E1][E2]...[En]. Если p обозначает переменную-указатель, p^ обозначает переменную, на которую ссылается p. Если r обозначает запись, то r.f обозначает поле f из записи r. Если p обозначает указатель, p.f обозначает поле f записи p^, т.е. точка подразумевает разыменование, и p.f означает p^.f.

Охрана типа v(Т0) обеспечивает, чтобы v имел тип T0, т.е. охрана типа прекращает выполнение программы, если v имеет тип отличный от T0. Охрана применима, если:

  1. T0 является расширением описанного типа T в v, и если
  2. v — переменный параметр типа запись, или v — указатель.
обозначение  = весьИдент {селектор}.
селектор     = "." идент | "[" РядВыражений "]" | "^" | "(" весьИдент ")".
РядВыражений = выражение {"," выражение}.

Если обозначенный объект является переменной, то обозначение ссылается на текущее значение переменной. Если объект является процедурой, обозначение без списка параметров ссылается на эту процедуру. Если за обозначением следует список параметров (возможно, пустой), обозначением подразумевает активацию процедуры и обозначает возвращаемый результат её исполнения. Фактические параметры (и их типы) должны соответствовать формальным параметрам, указанным в описании процедуры (см. Гл. 10).

Примеры обозначений (см. примеры в Гл. 7):

i (INTEGER)
a[i] (REAL)
w[3].ch (CHAR)
t.key (INTEGER)
t.left.right (Tree)
t(CenterNode).subnode(Tree)

8.2 Операции

В синтаксисе выражений различаются четыре класса операций с разным приоритетом (силой связывания). Операция ~ имеет наивысший приоритет, за которой следуют мультипликативные операции, аддитивные операции и отношения. Операции одного приоритета выполняются слева направо. Например, x–y–z означает (x–y)–z.

 выражение            = ПростоеВыражение [отношение ПростоеВыражение].
 отношение            = "=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS.
 ПростоеВыражение     =  ["+"|"−"] слагаемое {ОперацияСложения слагаемое}.
 ОперацияСложения     =  "+" | "−" | OR.
 слагаемое            = множитель {ОперацияУмножения множитель}.
 ОперацияУмножения    = "*" | "/" | DIV | MOD | "&" .
 множитель            = число | строка | NIL | TRUE | FALSE | множество |
   обозначение [ФактическиеПараметры] | "(" выражение ")" | "~" множитель.
 множество            = "{" [элемент {"," элемент}] "}".
 элемент              = выражение [".." выражение].
 ФактическиеПараметры = "(" [РядВыражений] ")" .

Множество {m..n} значит {m, m+1, ..., n-1, n}, а если m > n, то пустое множество. Доступные операторы перечислены в таблицах ниже. В некоторых случаях несколько разных операций обозначаются одной и той же лексемой. В этих случаях фактическая операция определяется типом операндов.

8.2.1 Логические операции

лексемарезультат
OR логическая дизъюнкция
& логическое соединение
~ отрицание

Эти операции применяются к операндам BOOLEAN и дают результат BOOLEAN.

р OR q означает«если р, то TRUE, иначе q»
p & q означает«если р, то q, иначе FALSE»
~ p означает«не p»

8.2.2 Арифметические операции

лексемарезультат
+ сумма
- разность
* произведение
/ вещественное деление
DIV деление нацело
MOD остаток

Операции +, -, * и / применяются к операндам числовых типов. Оба операнда должны быть одного типа, что также определяет тип результата. При использовании в качестве унарных операций «-» обозначает инверсию знака, а «+» обозначает операцию идентичности.

Операции DIV и MOD применяются только к целочисленным операндам. Пусть q = x DIV y и r = x MOD y. Тогда множитель q и остаток r определяются уравнением

X = q*y + r         0 ≤ r < y

8.2.3 Операции над множествами

лексемарезультат
+ объединение
- разность
* пересечение
/ cимметрическая разность множеств

Когда используется с одним операндом типа SET, знак минус обозначает дополнение множества.

8.2.4 Отношения

лексемаотношение
= равно
# неравно
< меньше
<= меньше или равно
> больше
>= больше или равно
IN членство в наборе
IS проверка типа

Отношения являются логическими операциями. Отношение упорядочения <, <=, >, >= применяется к числовым типам, CHAR и литерным массивам. Отношения = и # применимы также к типам BOOLEAN, SET, указателям и процедурным типам.

x IN s обозначает «x является элементом s». x должен иметь тип INTEGER и s должен быть типом SET.

v IS T означает «v имеет тип T» и называется проверкой типа. Это применимо, если

  1. T — расширение описанного типа T0 для v, и если
  2. v — переменный параметр типа записи или v — указатель.

Полагая, например, что T является расширением T0 и, что v является указателем на тип T0, тогда проверка v IS T определяет, является ли фактически назначенная переменная (не только T0, но и также) типом T. Значение NIL IS T не определено.

Примеры выражений (см. примеры в Гл. 7):

1987 (INTEGER)
i DIV 3 (INTEGER)
~ p OR q (BOOLEAN)
(I + j) * (i-j) (INTEGER)
s - {8, 9, 13} (SET)
a[i+j] * a [i-j] (REAL)
(0 <= i) & (i < 100)(BOOLEAN)
t.key = 0 (BOOLEAN)
K IN {i .. j-1} (BOOLEAN)
T IS CenterNode (BOOLEAN)

9. Операторы

Операторы обозначают действия. Есть элементарные и структурные операторы. Элементарные операторы не включают частей, которые сами являются операторами. Это присваивание и вызов процедуры. Структурные операторы состоят из частей, которые сами являются операторами. Они используются, чтобы выразить последовательное и условное, выборочное и повторное исполнение. Операторы также могут быть пустыми, и в этом случае они не означают никаких действий. Пустой оператор включен для того, чтобы ослабить правила пунктуации в последовательностях операторов.

 оператор  =
   [ присваивание | ВызовПроцедуры
   | ОператорIf | ОператорCase | ОператорWhile
   | ОператорRepeat | ОператорFor].

9.1 Присваивания

Присваивание служит для замены текущего значения переменной на новое значение, заданное выражением. Оператор присваивания записывается как «:=» и произносится как становится.

присваивание  =  переменная ":=" выражение.

Если значение параметра структурировано (имеет тип массив или запись), никакого присваивания ему или его элементам не допускаются. Для импортированных переменных также не допускаются присваивания.

Тип выражения должен быть таким же, как у обозначений. Имеют место следующие исключения:

  1. Константу NIL можно присвоить переменным любого типа указателя или процедуры.
  2. Строки могут быть присвоены любому массиву литер, если количество литер в строке меньше, чем количество литер в массиве. (Добавляется нулевая литера). Строки, состоящие из одной литеры, также могут быть присвоены переменным типа CHAR.
  3. В случае записей тип источника должен быть расширением типа адресата.
  4. Открытый массив может быть присвоен массиву равного базового типа.

Примеры присваиваний (см. примеры в Гл. 7):

i := 0
p := i = j
x := FLT(i + 1)
k := (i + j) DIV 2
f := log2
s := {2, 3, 5, 7, 11, 13}
a[i] := (x+y) * (x-y)
t.key := i
w[i+1].ch := "A"

9.2 Вызовы процедур

Вызов процедуры активирует её. Он может содержать список фактических параметров, подставлямых на место соответствующих формальных параметров, определённых в описании процедуры (см. Гл. 10). Соответствие проводится по положению в списках фактических и формальных параметров. Имеются два вида параметров: переменные и значения.

В случае переменных параметров, фактический параметр должен быть обозначением, обозначающим переменную. Если он обозначает элемент структурной переменной, селектор вычисляется, когда фактическая формальная замена / параметр имеет место, т.е. перед выполнением процедуры. Если параметр является параметром значения, соответствующий фактический параметр должен быть выражением. Это выражение вычисляется до активации процедуры, и результирующее значение присваивается формальному параметру, который теперь представляет собой локальную переменную (см. также 10.1).

ВызовПроцедуры  =  обозначение [ФактическиеПараметры].

Примеры вызова процедур:

ReadInt(i)    (см. Гл. 10)
WriteInt(2*j + 1, 6)
INC(w[k].count)

9.3 Последовательности операторов

Последовательности разделяемых точкой с запятой операторов означают очерёдность заданных ими действий.

РядОператоров  =  оператор {";" оператор}.

9.4 Операторы If

ОператорIf = IF выражение THEN
  РядОператоров
{ELSIF выражение THEN
  РядОператоров}
[ELSE
  РядОператоров]
END.

Операторы If определяют условное выполнение охраняемых операторов. Логическое выражение, предшествующие последовательности операторов, будем называть условием. Условия проверяются последовательно одно за другим, пока очередное не окажется равным TRUE, после чего выполняется связанная с этим условием последовательность операторов. Если ни одно условие не удовлетворено, выполняется последовательность операторов, записанная после слова ELSE, если оно имеется.

Пример:

IF (ch >= "A") & (ch <= "Z") THEN ReadIdentifier
ELSIF (ch >= "0") & (ch <= "9") THEN ReadNumber
ELSIF ch = 22X THEN ReadString
END

9.5 Операторы Case

Операторы Case определяют выбор и выполнение последовательности операторов по значению выражения. Сначала вычисляется выбирающее выражение, а затем выполняется та последовательность операторов, чей список меток варианта содержит полученное значение. Если выбирающее выражение имеет тип INTEGER или CHAR, то все метки должны быть целыми или строками из одной литеры, соответственно.

ОператорCase = CASE выражение OF вариант {" | " вариант} END.
вариант = [СписокМетокВарианта ":" РядОператоров].
СписокМетокВарианта = МеткиВарианта {"," МеткиВарианта }.
МеткиВарианта = метка [".." метка].
метка = целое | строка | весьИдент.

Пример:

CASE k OF
    0: x := x + y
  | 1: x := x − y
  | 2: x := x * y
  | 3: x := x / y
END

Тип T оценочного выражения (оценочной переменной) также может быть записью или указателем. Тогда метки должны быть расширениями T, и в операторах Si помечены как Ti, и оценочные переменные рассматриваются как тип Ti.

Пример:

TYPE R = RECORD a: INTEGER END ;
   R0 = RECORD (R) b: INTEGER END ;
   R1 = RECORD (R) b: REAL END ;
   R2 = RECORD (R) b: SET END ;
   P = POINTER TO R;
   P0 = POINTER TO R0;
   P1 = POINTER TO R1;
   P2 = POINTER TO R2;
VAR p: P;
CASE p OF
   P0: p.b := 10 |
   P1: p.b := 2.5 |
   P2: p.b := {0, 2}
END

9.6 Операторы While

Операторы While задают повторение. Если любое из логических выражений (условий) даёт TRUE, соответствующая последовательность операторов выполняется. Условия вычисляются и выполнение продолжается, пока хотя бы одно из логических выражений даёт TRUE.

ОператорWhile = WHILE выражение DO
                  РядОператоров
               {ELSIF выражение DO
                  РядОператоров}
                END.

Примеры:

WHILE j > 0 DO
   j := j DIV 2;
   i := i + 1
END
WHILE (t # NIL) & (t.key # i) DO
   t := t.left
END
WHILE m > n DO m := m - n
ELSIF n > m DO n := n - m
END

9.7 Операторы Repeat

Оператор Repeat задаёт повторное выполнение последовательности операторов до удовлетворения условия, заданного логическим выражением. Последовательность операторов выполняется по крайней мере один раз.

ОператорRepeat = REPEAT
                   РядОператоров
                 UNTIL выражение.

9.8 Операторы For

Оператор For определяет повторное выполнение последовательности операторов фиксированное число раз для прогрессии значений целочисленной переменной, называемой управляющей переменной оператора for.

ОператорFor =
  FOR идент ":=" выражение TO выражение [BY КонстВыражение] DO
    РядОператоров
  END.

Оператор

FOR v := beg TO end BY inc DO S END

если inc > 0, равнозначен

v := beg;
WHILE v <= end DO S; v := v + inc END

а если inc < 0, то равнозначен

v := beg;
WHILE v >= end DO S; v := v + inc END

Типы v, beg и end должны быть INTEGER, и inc должно быть целым (константным выражением). Если шаг не указан, то он полагается равным 1.

10. Описания процедур

Описание процедуры состоит из заголовка процедуры и тела процедуры. Заголовок определяет имя процедуры, формальные параметры и тип результата (если он есть). Тело содержит описания и операторы. Имя процедуры повторяется в конце описания процедуры.

Имеются два вида процедур: собственно процедуры и процедуры-функции. Последние активизируются обозначением функции как части выражения и возвращают результат, который является операндом выражения. Собственно процедуры активизируются вызовом процедуры. Процедура-функция отличается в описании тем, что обозначает тип результата после списка параметров. Тело процедуры-функции должно содержать оператор возврата RETURN, который определяет ее результат.

Все константы, переменные, типы и процедуры, описанные внутри тела процедуры, локальны в процедуре. Значения локальных переменных неопределены до входа в процедуру. Поскольку процедуры тоже могут быть описаны как локальные объекты, описания процедур могут быть вложенными.

Вдобавок к их формальным параметрам и локально описанным объектам, объекты описанные глобально также видны в процедуре.

Вызов процедуры изнутри ее описания подразумевает рекурсивную активацию.

ОписаниеПроцедуры = ЗаголовокПроцедуры ";" ТелоПроцедуры идент.
ЗаголовокПроцедуры  = PROCEDURE идентОпр [ФормальныеПараметры].
ТелоПроцедуры       = РядОписаний [BEGIN РядОператоров]
                      [RETURN выражение] END.
РядОписаний      = [CONST {ОписаниеКонстант ";"}]
                      [TYPE {ОписаниеТипов ";"}]
                      [VAR {ОписаниеПеременных ";"}]
                      {ОписаниеПроцедуры ";"}.

10.1 Формальные параметры

Формальные параметры — это идентификаторы, обозначающие фактические параметры, указанные в вызове процедуры. Соответствие между формальными и фактическими параметрами устанавливается при вызове процедуры. Имеются два вида параметров: значения и переменные. Параметр-переменная соответствуют фактическому параметру, являющемуся переменной, и обозначают эту переменную. Параметр-значение соответствует фактическому параметру, являющемуся выражением, и обозначает его значение, которое не может быть изменено присваиванием. Однако, если параметр-значение базового типа, то он представляет локальную переменную, которой изначально присвоено значение фактического выражения.

Тип параметра обозначен в списке формальных параметров: параметры-переменные обозначаются лексемой VAR, а параметры-значения — отсутствием префикса.

Процедура-функция без параметров должна иметь пустой список параметров. Она должна вызываться обозначением функции, чей список фактических параметров также пуст.

Формальные параметры локальны для процедуры, т.е. их область видимости — это текст программы, образовывающий описание процедуры.

ФормальныеПараметры = "(" [РазделФП {";" РазделФП }] ")" [":" весьИдент].
РазделФП            = [VAR] идент {"," идент} ":" ФормальныйТип.
ФормальныйТип       = {ARRAY OF} весьИдент.

Тип каждого формального параметра определён в списке параметров. Для параметров-переменных он должен быть идентичным соответствующему типу фактического параметра, кроме записей, когда он должен базовым типом соответствующего фактического параметра.

Если тип формального параметра определён как

ARRAY OF T

то такой параметр называется открытым массивом, и соответствующий фактический параметр может быть произвольной длины.

Если формальный параметр определён как процедурный тип, то соответствующий фактический параметр должен быть либо процедурой, которая описана глобально, либо переменной (или параметром) процедурного типа. Также в этом случае фактический параметр не может быть предопределенной процедурой. Тип результата процедуры не может быть ни записью, ни массивом.

Примеры описания процедур:

PROCEDURE ReadInt(VAR x: INTEGER);
  VAR i: INTEGER;
      ch: CHAR;
BEGIN
  i := 0; Read(ch);
  WHILE ("0" <= ch) & (ch <= "9") DO
    i := 10 * i + (ORD(ch) - ORD("0"));
    Read(ch)
  END;
  x := i
END ReadInt
PROCEDURE WriteInt(x: INTEGER); (* 0 ≤ x < 105 *)
  VAR i: INTEGER;
      buf: ARRAY 5 OF INTEGER;
BEGIN
  i := 0;
  REPEAT
    buf[i] := x MOD 10;
    x := x DIV 10;
    INC(i)
  UNTIL x = 0;

  REPEAT
    DEC(i);
    Write(CHR(buf[i] + ORD("0")))
  UNTIL i = 0
END WriteInt
PROCEDURE log2(x: INTEGER): INTEGER;
  VAR y: INTEGER; (* полагается x > 0 *)
BEGIN
  y := 0;
  WHILE x > 1 DO
    x := x DIV 2;
    INC(y)
  END;
  RETURN y
END log2

10.2 Предопределённые процедуры-функции

Следующая таблица содержит список предопределённых процедур. Некоторые процедуры — обобщенные, т.е. применимые к нескольким типам операндов. Буква v обозначает переменную, x и n — выражения, T — тип.

Процедуры-функции:

Имя Типы аргументовТип результата Функция
ABS(x) x: числовой типтип x абсолютное значение
ODD(x) x: INTEGER BOOLEAN x MOD 2 = 1
LEN(v) v: array INTEGER длина v
LSL(x, n) x, n: INTEGER INTEGER логический сдвиг влево, x * 2n
ASR(x, n) x, n: INTEGER INTEGER сдвиг вправо со знаком, x DIV 2n
ROR(x, n) x, n: INTEGER INTEGERвращение x вправо на n бит

Функции приведения типа:

Имя Типы аргументовТип результата Функция
FLOOR(x)REAL INTEGER округление к меньшему
FLT(x) INTEGER REAL тождество
ORD(x) CHAR, BOOLEAN, SETINTEGER порядковый номер x
CHR(x) INTEGER CHAR литера с порядковым номером x

Собственно процедуры:

Имя Типы аргументов Функция
INC(v) INTEGER v := v + 1
INC(v, n) INTEGER v := v + n
DEC(v) INTEGER v := v - 1
DEC(v, n) INTEGER v := v - n
INCL(v, x)v: SET; x: INTEGER v := v + {x}
EXCL(v, x)v: SET; x: INTEGER v := v - {x}
NEW(v) тип указатель размещение v^
ASSERT(b) BOOLEAN прерывание, если ~b
PACK(x, n)REAL; INTEGER упаковка x и n в x
UNPK(x, n)REAL; INTEGER распаковка из x в x и n

Функция FLOOR(x) возвращает наибольшее целое не большее чем x.

FLOOR(1.5) = 1     FLOOR(-1.5) = -2

Параметр n процедуры PACK представляет экспоненту x. PACK(x, y) равнозначно x := x * 2y. UNPK является обратной операцией. Результирующее x нормализовано, так что 1,0 ≤ x < 2,0.

11. Модули

Модуль — совокупность описаний констант, типов, переменных и процедур вместе с последовательностью операторов, предназначенных для присваивания начальных значений переменным. Модуль обычно представляет собой текст, который возможно компилировать как целое (единица компиляции).

Модуль        = МODULE идент ";" [СписокИмпорта] РядОписаний
                [BEGIN РядОператоров] END идент ".".
СписокИмпорта = IMPORT Импорт {"," Импорт} ";".
Импорт        = [идент ":="] идент.

В списке импорта указаны модули, клиентом которых является модуль. Если идентификатор x экспортируется из модуля M, и если M указан в списке импорта модуля, тогда обращение к x осуществляется как M.x. Если в списке импорта используется форма «M := M1», экспортируемый объект x, описанный в M1, вызывается в импортирующем модуле как M.x.

Идентификаторы, которые должны быть видны в клиентских модулях, т.е. подлежащие экспорту, должны быть отмечены звездочкой (меткой экспорта) в их описании. Переменные всегда экспортируются в режиме только для чтения.

Последовательность операторов, следующая за лексемой BEGIN, выполняется, когда модуль добавляется в систему (загружается). Отдельные процедуры (без параметров) могут быть активированы из системы, и эти процедуры служат в качестве команд.

Пример:

MODULE Out; (* экспортированные процедуры: Write, WriteInt, WriteLn *)
  IMPORT Texts, Oberon;
  VAR W: Texts.Writer;
  PROCEDURE Write*(ch: CHAR);
  BEGIN
    Texts.Write(W, ch)
  END Write;
  PROCEDURE WriteInt*(x, n: INTEGER);
    VAR i: INTEGER;
        a: ARRAY 16 OF CHAR;
  BEGIN
    i := 0;
    IF x < 0 THEN
      Texts.Write(W, "-");
      x := -x
    END;

    REPEAT
      a[i] := CHR(x MOD 10 + ORD("0"));
      x := x DIV 10;
      INC(i)
    UNTIL x = 0;

    REPEAT
      Texts.Write(W, " ");
      DEC(n)
    UNTIL n <= i;

    REPEAT
      DEC(i);
      Texts.Write(W, a[i])
    UNTIL i = 0
  END WriteInt;
  PROCEDURE WriteLn*;
  BEGIN
    Texts.WriteLn(W);
    Texts.Append(Oberon.Log, W.buf)
  END WriteLn;
BEGIN
  Texts.OpenWriter(W)
END Out.

11.1 Модуль SYSTEM

Опциональный модуль SYSTEM содержит определения, необходимые для программирования низкоуровневых операций, напрямую относящихся к ресурсам, характерным для данного компьютера и/или воплощения.

Они включают, к примеру, средства для доступа к устройствам, которые управляются компьютером, и, возможно, — средства для нарушения правил совместимости типов данных, иначе запрещённых по определению языка.

Есть две причины для предоставления таких средств в Модуле SYSTEM; (1) Их значения зависят от воплощения, то есть не выводятся из определения языка, и (2) они могут повредить систему (например, PUT). Настоятельно рекомендуется ограничить использование этих средств особыми низкоуровневыми модулями, так как такие модули по своей природе являются непереносимыми и не типобезопасными. Однако они легко распознаются по идентификатору SYSTEM в списке импорта модуля. Следующие определения общеприменимы. Однако, отдельные воплощения могут включать в свои модули SYSTEM дополнительные определения, характерные для специфического нижележащего компьютера. В нижележащем v обозначает переменную, x, a и n — выражения.

Процедуры-функции:

Имя Типы аргументовТип результата Функция
ADR(v) любой INTEGER адрес переменной v
SIZE(T) любой тип INTEGER размер в байтах
BIT(a, n) a, n: INTEGER BOOLEAN бит n из mem[a]

Собственно процедуры:

Имя Типы аргументов Функция
GET(a, v) a: INTEGER; v: любой базовый типv := mem[a]
PUT(a, x) a: INTEGER; x: любой базовый типmem[a] := x
COPY(src, dst, n) все INTEGER копирует подряд n слов из src в dst

Ниже приведены дополнительные процедуры, поддерживаемые компилятором для RISC-процессора:

Процедуры-функции:

Имя Типы аргументов Тип результатаФункция
VAL(T, n) скаляр T тождество
ADC(m, n) INTEGER INTEGER сложение с флагом переноса C
SBC(m, n) INTEGER INTEGER вычитание с флагом переноса C
UML(m, n) INTEGER INTEGER беззнаковое умножение
COND(n) INTEGER BOOLEAN IF Cond(n) THEN ...

Собственно процедуры:

Имя Тип аргумента Функция
LED(n)INTEGER показать n на светодиодах

Приложение

Синтаксис Оберона

буква        =  "A" | "B" | ... | "Z" | "a" | "b" | ... | "z".
цифра        =  "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".
шестнЦифра   =  цифра | "A" | "B" | "C" | "D" | "E" | "F".
идент        =  буква {буква | цифра}.
весьИдент    =  [идент "."] идент.
идентОпр     =  идент ["*"].
целое        =  цифра {цифра} | цифра {шестнЦифра} "H".
дробь        =  цифра {цифра} "." {цифра} [порядок].
порядок      =  ("E") ["+" | "-"] цифра {цифра}.
число        =  целое | дробь.
строка       = """ {литера} """ | цифра {шестнЦифра} "X".
ОписаниеКонстанты =  идентОпр "=" КонстВыражение.
КонстВыражение    =  выражение.
ОписаниеТипа    =  идентОпр "=" тип.
тип             =  весьИдент | ТипМассив | ТипЗапись | ТипУказатель | ТипПроцедура.
ТипМассив       =  ARRAY длина {"," длина} OF тип.
длина           =  КонстВыражение.
ТипЗапись       =  RECORD ["(" БазовыйТип ")"] [ПоследСпискаПолей] END.
БазовыйТип      =  весьИдент.
ПоследСпискаПолей =  СписокПолей {";" СписокПолей}.
СписокПолей     =  СписокИдент ":" тип.
СписокИдент     =  идентОпр {"," идентОпр}.
ТипУказатель    =  POINTER TO тип.
ТипПроцедура    =  PROCEDURE [ФормальныеПараметры].
ОписаниеПеременных = СписокИдент ":" тип.
выражение         = ПростоеВыражение [отношение ПростоеВыражение].
отношение         = "=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS.
ПростоеВыражение  =  ["+"|"-"] слагаемое {ОперацияСложения слагаемое}.
ОперацияСложения  =  "+" | "-" | OR.
слагаемое         = множитель {ОперацияУмножения множитель}.
ОперацияУмножения = "*" | "/" | DIV | MOD | "&" .
множитель         = число | строка | NIL | TRUE | FALSE | множество |
                      обозначение [ФактическиеПараметры] |
                      "(" выражение ")" | "~" множитель.
обозначение       = весьИдент {селектор}.
селектор          = "." идент | "[" РядВыражений "]" | "^" | "(" весьИдент ")".
множество         = "{" [элемент {"," элемент}] "}".
элемент           = выражение [".." выражение].
РядВыражений      = выражение {"," выражение}.
ФактическиеПараметры  = "(" [РядВыражений] ")" .
оператор            = [присваивание | ВызовПроцедуры | ОператорIf | ОператорCase |
                      ОператорWhile | ОператорRepeat | ОператорFor].
присваивание        = обозначение ":=" выражение
ВызовПроцедуры      = обозначение [ФактическиеПараметры].
РядОператоров       = оператор {";" оператор}.
ОператорIf          = IF выражение THEN
                         РядОператоров
                      {ELSIF выражение THEN
                         РядОператоров}
                      [ELSE
                         РядОператоров]
                      END.
ОператорCase        = CASE выражение OF вариант {" | " вариант} END.
вариант             = [СписокМетокВарианта ":" РядОператоров].
СписокМетокВарианта = МеткиВарианта {"," МеткиВарианта }.
МеткиВарианта       = метка [".." метка].
метка               = целое | строка | весьИдент.
ОператорWhile       = WHILE выражение DO РядОператоров
                      {ELSIF выражение DO РядОператоров} END.
ОператорRepeat      = REPEAT РядОператоров UNTIL выражение.
ОператорFor         = FOR идент ":=" выражение TO выражение [BY КонстВыражение] DO
                      РядОператоров END.
ОписаниеПроцедуры   = ЗаголовокПроцедуры ";" ТелоПроцедуры идент.
ЗаголовокПроцедуры  = PROCEDURE идентОпр [ФормальныеПараметры].
ТелоПроцедуры       = РядОписаний [BEGIN РядОператоров]
                      [RETURN выражение] END.
РядОписаний         = [CONST {ОписаниеКонстант ";"}]
                      [TYPE {ОписаниеТипов ";"}]
                      [VAR {ОписаниеПеременных ";"}]
                      {ОписаниеПроцедуры ";"}.
ФормальныеПараметры = "(" [РазделФП {";" РазделФП }] ")" [":" весьИдент].
РазделФП            = [VAR] идент {"," идент} ":" ФормальныйТип.
ФормальныйТип       = {ARRAY OF} весьИдент.
модуль              = MODULE идент ";" [СписокИмпорта] РядОписаний
                      [BEGIN РядОператоров] END идент ".".
СписокИмпорта       = IMPORT импорт {"," импорт} ";".
импорт              = [идент ":="] идент.