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 require to commit the definition when a general commitment appears as unwise.
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 words written in capital letters, so-called reserved words. Syntactic rules (productions) are marked by a $ sign at the left margin of the line.
$ ident = letter {letter | digit}.
Examples:
x scan Oberon GetSymbol firstLetter
A real number always contains a decimal point. Optionally it may also contain a decimal scale factor. The letter E (or D) is pronounced as "times ten to the power of". A real number is of type REAL, unless it has a scale factor containing the letter D; in this case it is of type LONGREAL.
$ number = integer | real. $ integer = digit {digit} | digit {hexDigit} "H" . $ real = digit {digit} "." {digit} [ScaleFactor]. $ ScaleFactor = ("E" | "D") ["+" | "-"] 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 0.57712566D-6 = 0.00000057712566
$ CharConstant = """ character """ | digit {hexDigit} "X".
$ string = """ {character} """ .
Examples:
"OBERON" "Don't worry!"
+ := ARRAY IS TO - ^ BEGIN LOOP TYPE * = CASE MOD UNTIL / # CONST MODULE VAR ~ < DIV NIL WHILE & > DO OF WITH . <= ELSE OR , >= ELSIF POINTER ; .. END PROCEDURE | : EXIT RECORD ( ) IF REPEAT [ ] IMPORT RETURN { } IN THEN
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. The scope rule has the following amendments:
$ qualident = [ident "."] ident. $ identdef = ident ["*"].
The following identifiers are predefined; their meaning is defined in the indicated sections:
ABS (10.2) LEN (10.2) ASH (10.2) LONG (10.2) BOOLEAN (6.1) LONGINT (6.1) CAP (10.2) LONGREAL (10.2) CHAR (6.1) MAX (10.2) CHR (10.2) MIN (10.2) COPY (10.2) NEW (6.4) DEC (10.2) ODD (10.2) ENTIER (10.2) ORD (10.2) EXCL (10.2) REAL (6.1) FALSE (6.1) SET (6.1) HALT (10.2) SHORT (10.2) INC (10.2) SHORTINT (6.1) INCL (10.2) SIZE (10.2) INTEGER (6.1) TRUE (6.1)
$ ConstantDeclaration = 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 Section 8).
Examples of constant declarations are:
N = 100 limit = 2*N -1 all = {0 .. WordSize-1}
$ 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
LONGREAL >= REAL >= LONGINT >= INTEGER >= SHORTINT
Note: >= stands here for "is superset of or equal to".
$ 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
$ 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 T0 extends a type T, if it equals T, or if it directly extends an extension of T. Conversely, a type T is a base type of T0, if it equals T0, or if it is the direct base type of a base type of T0.
Examples of record types:
RECORD day, month, year: INTEGER END RECORD name, firstname: ARRAY 32 OF CHAR; age: INTEGER; salary: REAL END
$ PointerType = POINTER TO type.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; 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.
$ ProcedureType = PROCEDURE [FormalParameters].
$ 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 Section 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: TreeVariables of a pointer type T0 and VAR-parameters of a record type T0 may assume values whose type T1 is an extension of their declared type T0.
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 an integer type. 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, and p[E] denotes the element of p^ with index E.
The typeguard v(T0) asserts that v is of type T0, i.e. it aborts program execution, if v is not of type T0. The guard is applicable, if
$ designator = qualident {"." 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 Section 10).
Examples of designators (see examples in Section 7):
i (INTEGER) a[i] (REAL) w[3].ch (CHAR) t.key (INTEGER) t.left.right (Tree) t(CenterNode).subnode (Tree)
$ expression = SimpleExpression [relation SimpleExpression]. $ relation = "=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS. $ SimpleExpression = ["+"|"-"] term {AddOperator term}. $ AddOperator = "+" | "-" | OR . $ term = factor {MulOperator factor}. $ MulOperator = "*" | "/" | DIV | MOD | "&" . $ factor = number | CharConstant | string | NIL | set | $ designator [ActualParameters] | "(" expression ")" | "~" factor. $ set = "{" [element {"," element}] "}". $ element = expression [".." expression]. $ ActualParameters = "(" [ExpList] ")" .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
symbol result OR logical disjunction & logical conjunction ~ negationThese operators apply to BOOLEAN operands and yield a BOOLEAN result.
p 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
symbol result + sum - difference * product / quotient DIV integer quotient MOD modulusThe operators +, -, *, and / apply to operands of numeric types. The type of the result is that operand's type which includes the other operand's type, except for division (/), where the result is the real type which includes both operand types. When used as operators with a single operand, - denotes sign inversion and + denotes the identity operation.
The operators DIV and MOD apply to integer operands only. They are related by the following formulas defined for any dividend x and positive divisors y:
x = (x DIV y) * y + (x MOD y) 0 <= (x MOD y) < y
8.2.3. Set operators
symbol result + union - difference * intersection / symmetric set differenceThe monadic minus sign denotes the complement of x, i.e. -x denotes the set of integers between 0 and MAX(SET) which are not elements of x.
x - y = x * (-y) x / y = (x-y) + (y-x)
8.2.4. Relations
symbol relation = equal # unequal < less <= less or equal > greater >= greater or equal IN set membership IS type testRelations are Boolean. The ordering relations <, <=, >, and >= apply to the numeric types, CHAR, and character arrays (strings). The relations = and # also apply to the type BOOLEAN and to set, pointer, and procedure types. x IN s stands for "x is an element of s". x must be of an integer type, and s of type SET. v IS T stands for "v is of type T" and is called a type test. It is applicable, if
Examples of expressions (refer to examples in Section 7):
1987 (INTEGER) i DIV 3 (INTEGER) ~p OR q (BOOLEAN) (i+j) * (i-j) (INTEGER) s - {8, 9, 13} (SET) i + x (REAL) 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)
$ statement = [assignment | ProcedureCall | $ IfStatement | CaseStatement | WhileStatement | RepeatStatement | $ LoopStatement | WithStatement | EXIT | RETURN [expression] ].
$ assignment = designator ":=" expression.The type of the expression must be included by the type of the variable, or it must extend the type of the variable. The following exceptions hold:
Examples of assignments (see examples in Section 7):
i := 0 p := i = j x := i + 1 k := log2(i+j) F := log2 s := {2, 3, 5, 7, 11, 13} a[i] := (x+y) * (x-y) t.key := i w[i+1].ch := "A"
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 Section 10) WriteInt(j*2+1, 6) INC(w[k].count)
$ StatementSequence = statement {";" statement}.
$ 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
$ CaseStatement = CASE expression OF case {"|" case} $ [ELSE StatementSequence] END. $ case = [CaseLabelList ":" StatementSequence]. $ CaseLabelList = CaseLabels {"," CaseLabels}. $ CaseLabels = ConstExpression [".." ConstExpression].
Example:
CASE ch OF "A" .. "Z": ReadIdentifier | "0" .. "9": ReadNumber | 22X : ReadString ELSE SpecialCharacter END
$ WhileStatement = WHILE 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
$ RepeatStatement = REPEAT StatementSequence UNTIL expression.
$ LoopStatement = LOOP StatementSequence END.
Example:
LOOP IF t1 = NIL THEN EXIT END ; IF k < t1.key THEN t2 := t1.left; p := TRUE ELSIF k > t1.key THEN t2 := t1.right; p := FALSE ELSE EXIT END ; t1 := t2 ENDAlthough while and repeat statements can be expressed by loop statements containing a single exit statement, the use of while and repeat statements is recommended in the most frequently occurring situations, where termination depends on a single condition determined either at the beginning or the end of the repeated statement sequence. The loop statement is useful to express cases with several termination conditions and points.
Function procedures require the presence of a return statement indicating the result value. There may be several, although only one will be executed. In proper procedures, a return statement is implied by the end of the procedure body. An explicit return statement therefore appears as an additional (probably exceptional) termination point.
An exit statement consists of the symbol EXIT. It specifies termination of the enclosing loop statement and continuation with the statement following that loop statement. Exit statements are contextually, although not syntactically bound to the loop statement which contains them.
$ WithStatement = WITH qualident ":" qualident DO StatementSequence END .
Example:
WITH t: CenterNode DO name := t.name; L := t.subnode END
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. The function procedure is distinguished in the declaration by indication of the type of its result following the parameter list. Its body must contain a RETURN statement 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 in the environment of the procedure are also visible in the procedure (with the exception of those objects that have the same name as an object declared locally).
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] END. $ ForwardDeclaration = PROCEDURE "^" identdef [FormalParameters]. $ DeclarationSequence = {CONST {ConstantDeclaration ";"} | $ TYPE {TypeDeclaration ";"} | VAR {VariableDeclaration ";"}} $ {ProcedureDeclaration ";" | ForwardDeclaration ";"}.A forward declaration serves to allow forward references to a procedure that appears later in the text in full. The actual declaration - which specifies the body - must indicate the same parameters and result type (if any) as the forward declaration, and it must be within the same scope.
An asterisk following the symbol PROCEDURE is a hint to the compiler and specifies that the procedure is to be usable as parameter and assignable to variables. (Depending on the implementation, the hint may be optional or required.).
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 | ProcedureType.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. For value parameters, the rule of assignment holds (see 9.1). If the formal parameter's type is specified as ARRAY OF T the parameter is said to be an open array parameter, and the corresponding actual parameter may be any array with element type T.
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.0E5 *) 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
Name | Argument type | Result type | Function |
ABS(x) | numeric type | type of x | absolute value |
ODD(x) | integer type | BOOLEAN | x MOD 2 = 1 |
CAP(x) | CHAR | CHAR | corresponding capital letter |
ASH(x, n) | x, n: integer type | LONGINT | x * 2n, arithmetic shift |
LEN(v, n) | v: array n: integer type | LONGINT | length of v in dimension n |
LEN(v) | array type | LONGINT | LEN(v, 0) |
MAX(T) | T = basic type T = SET | T INTEGER |
maximum value of type T maximum element of sets |
MIN(T) | T = basic type T = SET | T INTEGER |
minimum value of type T 0 |
SIZE(T) | T = any type | integer type | no. of bytes required by T |
Name | Argument type | Result type | Function |
ORD(x) | CHAR | INTEGER | ordinal number of x |
CHR(x) | integer type | CHAR | character with ordinal number x |
SHORT(x) | LONGINT INTEGER LONGREAL |
INTEGER SHORTINT REAL | identity (truncation possible) |
LONG(x) | SHORTINT INTEGER REAL |
INTEGER LONGINT LONGREAL | identity |
ENTIER(x) | real type | LONGINT | largest integer
not greater than x. Note that ENTIER(i/j) = i DIV j |
Name | Argument types | Function |
INC(v) | integer type | v := v+1 |
INC(v, x) | integer type | v := v+x |
DEC(v) | integer type | v := v-1 |
DEC(v, x) | integer type | v := v-x |
INCL(v, x) | v: SET x: integer type | v := v + {x} |
EXCL(v, x) | v: SET x: integer type | v := v - {x} |
COPY(x, v) | x: character array, string v: character array |
v := x |
NEW(v) | pointer type | allocate v^ |
HALT(x) | integer constant | terminate program execution |
In HALT(x), x is a parameter whose interpretation is left to the underlying system implementation.
$ module = MODULE 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, that object declared within M1 is referenced as M.x .
Identifiers that are to be visible in client modules, i.e. outside the declaring module, must be marked by an export mark in their declaration.
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: LONGINT); 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.
Module SYSTEM exports the data type BYTE. No representation of values is specified. Instead, certain compatibility rules with other types are given:
Name | Argument types | Result type | Function |
ADR(v) | any | LONGINT | address of variable v |
BIT(a, n) | a: LONGINT n: integer type | BOOLEAN | bit n of Mem[a] |
CC(n) | integer constant | BOOLEAN | Condition n (0 <= n < 16) |
LSH(x, n) | x: integer type or SET n: integer type |
type of x | logical shift |
ROT(x, n) | x: integer type or SET n: integer type |
type of x | rotation |
VAL(T, x) | T, x: any type | T | x interpreted as of type T |
Name | Argument types | Function |
GET(a, v) | a: LONGINT v: any basic type | v := Mem[a] |
PUT(a, x) | a: LONGINT x: any basic type | Mem[a] := x |
MOVE(s, d, n) | s, d: LONGINT n: integer type | Mem[d]
... Mem[d+n-1] := Mem[s] ... Mem[s+n-1] |
NEW(v, n) | v: any pointer type n: integer type |
allocate storage block of n bytes assign its address to v |
HTML Translation: A. Fischer and J. Gutknecht/ October 1999