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


Введение

Оберон-2 - язык программирования общего назначения, продолжающий традицию языков Паскаль и Modula-2. Его основные черты - блочная структура, модульность, раздельная компиляция, статическая типизация со строгим контролем соответствия типов (в том числе межмодульным), а также расширение типов и связанные с типами процедуры.
Расширение типов делает Оберон-2 объектно-ориентированным языком. Объект - это переменная абстрактного типа, содержащая данные (состояние объекта) и процедуры, которые оперируют этими данными. Абстрактные типы данных определены как расширяемые записи. Оберон-2 перекрывает большинство терминов объектно-ориентированных языков привычным словарем языков императивных, обходясь минимумом понятий в рамках тех же концепций.
Этот документ не является учебником программирования. Он преднамеренно краток. Его назначение - служить справочником для программистов, разработчиков компиляторов и авторов руководств. Если о чем-то не сказано, то обычно сознательно: или потому, что это следует из других правил языка, или потому, что потребовалось бы определять то, что фиксировать для общего случая представляется неразумным.
В приложении A определены некоторые термины, которые используются при описании правил соответствия типов Оберона-2. В тексте эти термины выделены курсивом, чтобы подчеркнуть их специальное значение (например, одинаковый тип).

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



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



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

Операции +, -, *, и / применимы к операндам числовых типов. Тип их результата - тип того операнда, который поглощает тип другого операнда, кроме деления (/), чей результат - наименьший вещественный тип, который поглощает типы обоих операндов. При использовании в качестве одноместной операции "-" обозначает перемену знака, а "+" - тождественную операцию. Операции DIV и MOD применимы только к целочисленным операндам. Они связаны следующими формулами, определенными для любого x и положительного делителя y:
x = (x DIV y) * y + (x MOD y)
0 < = (x MOD y) < y
Примеры:

x y x DIV y x MOD y
5 3 1 2
-5 3 -2 1



D1. Команды



D1. Команды


Команда - это любая процедура P, которая экспортируется модулем M и не имеет параметров. Она обозначается M.P и может быть активирована под таким именем из оболочки операционной системы. В Обероне пользователь вызывает команды вместо программ или модулей. Это дает лучшую структуру управления и предоставляет модули с несколькими точками входа. Когда вызывается команда M.P, модуль M динамически загружается, если он уже не был в памяти (см. D2) и выполняется процедура P. Когда P завершается, M остается загруженным. Все глобальные переменные и структуры данных, которые могут быть достигнуты через глобальные переменные-указатели в M, сохраняют значения. Когда P (или другая команда M) вызывается снова, она может продолжать использовать эти значения.
Следующий модуль демонстрирует использование команд. Он реализует абстрактную структуру данных Counter, которая содержит переменную-счетчик и обеспечивает команды для увеличения и печати его значения.
MODULE Counter;
IMPORT Texts, Oberon;
VAR
counter: LONGINT;
w: Texts.Writer;
PROCEDURE Add*; (* получает числовой аргумент из командной строки *)
VAR s: Texts.Scanner;
BEGIN
Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos);
Texts.Scan(s);
IF s.class = Texts.Int THEN INC(counter, s.i) END
END Add;
PROCEDURE Write*;
BEGIN
Texts.WriteInt(w, counter, 5); Texts.WriteLn(w);
Texts.Append(Oberon.Log, w.buf)
END Write;
BEGIN counter := 0; Texts.OpenWriter(w)
END Counter.
Пользователь может выполнить следующие две команды:
Counter.Add n Добавляет значение n к переменной counter
Counter.Write Выводит текущее значение counter на экран

Так как команды не содержат параметров, они должны получать свои аргументы из операционной системы. Вообще команды вольны брать параметры отовсюду (например из текста после команды, из текущего выбранного фрагмента или из отмеченного окна просмотра). Команда Add использует сканер (тип данных, обеспечиваемый Оберон-системой) чтобы читать значение, которое следует за нею в командной строке.
Когда Counter.Add вызывается впервые, модуль Counter загружается и выполняется его тело. Каждое обращение Counter.Add n увеличивает переменную counter на n. Каждое обращение Counter.Write выводит текущее значение counter на экран.
Поскольку модуль остается загруженным после выполнения его команд, должен существовать явный способ выгрузить его (например, когда пользователь хочет заменить загруженную версию перекомпилированной версией). Оберон-система содержит команду, позволяющую это сделать.


D2. Динамическая загрузка модулей



D2. Динамическая загрузка модулей


Загруженный модуль может вызывать команду незагруженного модуля, задавая ее имя как строку. Специфицированный модуль при этом динамически загружается и выполняется заданная команда. Динамическая загрузка позволяет пользователю запустить программу как небольшой набор базисных модулей и расширять ее, добавляя последующие модули во время выполнения по мере необходимости.
Модуль M0 может вызвать динамическую загрузку модуля M1 без того, чтобы импортировать его. M1 может, конечно, импортировать и использовать M0, но M0 не должен знать о существовании M1. M1 может быть модулем, который спроектирован и реализован намного позже M0.


D3. Сбор мусора



D3. Сбор мусора


В Обероне-2 стандартная процедура NEW используется, чтобы распределить блоки данных в свободной памяти. Нет, однако, никакого способа явно освободить распределенный блок. Взамен Оберон-среда использует сборщик мусора чтобы найти блоки, которые больше не используются и сделать их снова доступными для распределения. Блок считается используемым только если он может быть достигнут через глобальную переменную-указатель по цепочке указателей. Разрыв этой цепочки (например, установкой указателя в NIL) делает блок утилизируемым.
Сборщик мусора освобождает программиста от нетривиальной задачи правильного освобождения структур данных и таким образом помогает избегать ошибок. Возникает, однако, необходимость иметь информацию о динамических данных во время выполнения (см. D5).


D4. Смотритель



D4. Смотритель


Интерфейс модуля (объявления экспортируемых объектов) извлекается из модуля так называемым смотрителем, который является отдельным инструментом среды Оберон. Например, смотритель производит следующий интерфейс модуля Trees из Гл. 11.
DEFINITION Trees;
TYPE
Tree = POINTER TO Node;
Node = RECORD
name: POINTER TO ARRAY OF CHAR;
PROCEDURE (t: Tree) Insert (name: ARRAY OF CHAR);
PROCEDURE (t: Tree) Search (name: ARRAY OF CHAR): Tree;
PROCEDURE (t: Tree) Write;
END;
PROCEDURE Init (VAR t: Tree);
END Trees.
Для типа запись смотритель также собирает все процедуры, связанные с этим типом, и показывает их заголовки в объявлении типа запись.


D5. Структуры данных времени выполнения



D5. Структуры данных времени выполнения


Некоторая информация о записях должна быть доступна во время выполнения: Динамический тип записей необходим для проверки и охраны типа. Таблица с адресами процедур, связанных с записью, необходима для их вызова. Наконец, сборщик мусора нуждается в информации о расположении указателей в динамически распределенных записях. Вся эта информация сохраняется в так называемых дескрипторах типа. Один дескриптор необходим во время выполнения для каждого типа записи. Ниже показана возможная реализация дескрипторов типа.
Динамический тип записи соответствует адресу дескриптора типа. Для динамически распределенных записей этот адрес сохраняется в так называемом теге типа, который предшествует фактическим данным записи и является невидимым для программиста. Если t - переменная типа CenterTree (см. ), рисунок D показывает одну из возможных реализаций структур данных времени выполнения.
D5. Структуры данных времени выполнения
Рисунок D переменная t типа CenterTree, запись t^, на которую она указывает, и дескриптор типа
Поскольку и таблица адресов процедур и таблица смещений указателей должны иметь фиксированное смещение относительно адреса дескриптора типа, и поскольку они могут расти, когда тип расширяется и добавляются новые процедуры и указатели, то таблицы размещены в противоположных концах дескриптора типа и растут в разных направлениях.
Связанная с типом процедура t.P вызывается как t^.tag^.ProcTab[IndexP]. Индекс таблицы процедур для каждой связанной с типом процедуры известен во время компиляции. Проверка типа v IS T транслируется в v^.tag^.BaseTypes [ExtensionLevelT] = TypeDescrAdrT. И уровень расширения типа запись (ExtensionLevelT), и адрес описателя типа (TypeDescrAdrT) известны во время компиляции. Например, уровень расширения Node - 0 (этот тип не имеет базового типа), а уровень расширения CenterNode равен 1.
[1] N.Wirth, J.Gutknecht: The Oberon System. Software Practice and Experience 19, 9, Sept. 1989
[2] M.Reiser: The Oberon System. User Guide and Programming Manual. Addison-Wesley, 1991
[3] C.Pfister, B.Heeb, J.Templ: Oberon Technical Notes. Report 156, ETH Zurich, March 1991


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



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


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

ФормальныеПараметры = "(" [СекцияФП {";" СекцияФП }] ")" [":" УточненныйИдент].
СекцияФП = [VAR] идент {"," идент} ":" Тип.

Пусть Tf - тип формального параметра f (не открытого массива) и Ta - тип соответствующего фактического параметра a. Для параметров-переменных Ta и Tf должны быть одинаковыми типами или Tf должен быть типом запись, а Ta - расширением Tf. Для параметров-значений а должен быть совместим по присваиванию с f (см. Прил. A).
Если Tf - открытый массив, то a должен быть совместимым массивом для f (см. Прил. A). Длина f становится равной длине a.
Примеры объявлений процедур:
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 <100000*)
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 WriteString (s: ARRAY OF CHAR);
VAR i: INTEGER;
BEGIN i := 0;
WHILE (i < LEN(s)) & (s[i] # 0X) DO Write(s[i]); INC(i) END
END WriteString;

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


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



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



OR логическая дизъюнкция p OR q "если p, то TRUE, иначе q"
& логическая конъюнкция p & q "если p то q, иначе FALSE"
~ отрицание ~p "не p"

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


Модули



11. Модули


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

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

Список импорта определяет имена импортируемых модулей. Если модуль A импортируется модулем M, и A экспортирует идентификатор x, то x упоминается внутри M как A.x. Если A импортируется как B:=A, объект x должен вызываться как B.x. Это позволяет использовать короткие имена-псевдонимы в уточненных идентификаторах. Модуль не должен импортировать себя. Идентификаторы, которые экспортируются (то есть должны быть видимы в модулях-импортерах) нужно отметить экспортной меткой в их объявлении (см. Главу 4).
Последовательность операторов после символа BEGIN выполняется, когда модуль добавляется к системе (загружается). Это происходит после загрузки импортируемых модулей. Отсюда следует, тот циклический импорт модулей запрещен. Отдельные (не имеющие параметров и экспортированные) процедуры могут быть активированы из системы. Эти процедуры служат командами (см. Приложение D1).
MODULE Trees; (* экспорт: Tree, Node, Insert, Search, Write, Init *)
IMPORT Texts, Oberon; (* экспорт только для чтения: Node.name *)
TYPE
Tree* = POINTER TO Node;
Node* = RECORD
name-: POINTER TO ARRAY OF CHAR;
left, right: Tree


END;

VAR w: Texts.Writer;

PROCEDURE (t: Tree) Insert* (name: ARRAY OF CHAR);
VAR p, father: Tree;
BEGIN p := t;
REPEAT father := p;
IF name = p.name^ THEN RETURN END;
IF name < p.name^ THEN p := p.left ELSE p := p.right END
UNTIL p = NIL;
NEW(p); p.left := NIL; p.right := NIL; NEW(p.name, LEN(name)+1); COPY(name, p.name^);
IF name < father.name^ THEN father.left := p ELSE father.right := p END
END Insert;

PROCEDURE (t: Tree) Search* (name: ARRAY OF CHAR): Tree;
VAR p: Tree;
BEGIN p := t;
WHILE (p # NIL) & (name # p.name^) DO
IF name < p.name^ THEN p := p.left ELSE p := p.right END
END;
RETURN p
END Search;

PROCEDURE (t: Tree) Write*;
BEGIN
IF t.left # NIL THEN t.left.Write END;
Texts.WriteString(w, t.name^); Texts.WriteLn(w); Texts.Append(Oberon.Log, w.buf);
IF t.right # NIL THEN t.right.Write END
END Write;

PROCEDURE Init* (t: Tree);
BEGIN NEW(t.name, 1); t.name[0] := 0X; t.left := NIL; t.right := NIL
END Init;

BEGIN Texts.OpenWriter(w)
END Trees.


Объявления и области действия



4. Объявления и области действия


Каждый идентификатор, встречающийся в программе, должен быть объявлен, если это не стандартный идентификатор. Объявления задают некоторые постоянные свойства объекта, например, является ли он константой, типом, переменной или процедурой. После объявления идентификатор используется для ссылки на соответствующий объект.
Область действия объекта x распространяется текстуально от точки его объявления до конца блока (модуля, процедуры или записи), в котором находится объявление. Для этого блока объект является локальным. Это разделяет области действия одинаково именованных объектов, которые объявлены во вложенных блоках. Правила для областей действия таковы:
  • Идентификатор не может обозначать больше чем один объект внутри данной области действия (то есть один и тот же идентификатор не может быть объявлен в блоке дважды);
  • Ссылаться на объект можно только изнутри его области действия;
  • Тип T вида POINTER TO T1 (см. ) может быть объявлен в точке, где T1 еще неизвестен. Объявление T1 должно следовать в том же блоке, в котором T является локальным;
  • Идентификаторы, обозначающие поля записи (см. ) или процедуры, связанные с типом, (см. ) могут употребляться только в обозначениях записи.

  • Идентификатор, объявленный в блоке модуля, может сопровождаться при своем объявлении экспортной меткой ("*" или "-"), чтобы указать, что он экспортируется. Идентификатор x, экспортируемый модулем M, может использоваться в других модулях, если они импортируют M (см. гл. 11). Тогда идентификатор обозначается в этих модулях М.x и называется уточненным идентификатором. Переменные и поля записей, помеченные знаком "-" в их объявлении, предназначены только для чтения в модулях-импортерах.

    УточнИдент = [идент "."] идент.
    ИдентОпр = идент ["*" | "-"].

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

    ABS () LEN ()
    ASH () LONG ()
    BOOLEAN () LONGINT ()
    CAP () LONGREAL ()
    CHAR () MAX ()
    CHR () MIN ()
    COPY () NEW ()
    DEC () ODD ()
    ENTIER () ORD ()
    EXCL () REAL ()
    FALSE () SET ()
    HALT () SHORT ()
    INC () SHORTINT ()
    INCL () SIZE ()
    INTEGER () TRUE ()



    Объявления констант



    5. Объявления констант


    Объявление константы связывает ее идентификатор с ее значением.

    ОбъявлениеКонстанты = ИдентОпр "=" КонстантноеВыражение.
    КонстантноеВыражение = Выражение.

    Константное выражение - это выражение, которое может быть вычислено по его тексту без фактического выполнения программы. Его операнды - константы (Гл. 8) или стандартные функции (Гл. ), которые могут быть вычислены во время компиляции.
    Примеры объявлений констант:
    N = 100
    limit = 2*N - 1
    fullSet = {MIN(SET) .. MAX(SET)}


    Объявления переменных



    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
    name: ARRAY 32 OF CHAR;
    count: INTEGER
    END
    t, c: Tree


    Объявления процедур



    10. Объявления процедур


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

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

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


    Объявления типа



    6. Объявления типа


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

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

    Примеры:
    Table = ARRAY N OF REAL
    Tree = POINTER TO Node
    Node = RECORD
    key : INTEGER;
    left, right: Tree
    END
    CenterTree = POINTER TO CenterNode
    CenterNode = RECORD (Node)
    width: INTEGER;
    subnode: Tree
    END
    Function = PROCEDURE(x: INTEGER): INTEGER


    Одинаковые типы



    Одинаковые типы


    Две переменные a и b с типами Ta и Tb имеют одинаковый тип, если
  • Ta и Tb оба обозначены одним и тем же идентификатором типа, или
  • Ta объявлен равным Tb в объявлении типа вида Ta = Tb, или
  • a и b появляются в одном и том же списке идентификаторов переменных, полей записи или объявлении формальных параметров и не являются открытыми массивами.



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



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


    +объединение
    - разность (x - y = x * (-y))
    * пересечение
    / симметрическая разность множеств (x / y = (x-y) + (y-x))
    Эти операции применимы к операндам типа SET и дают результат типа SET. Одноместный "минус" обозначает дополнение x, то есть -x это множество целых между 0 и MAX(SET), которые не являются элементами x. Операции с множествами не ассоциативны ((a+b)-c # a+(b-c)). Конструктор множества задает значение множества списком элементов, заключенным в фигурные скобки. Элементы должны быть целыми в диапазоне 0..MAX(SET). Диапазон a..b обозначает все целые числа в интервале [a, b].


    Операции



    Операции


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

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

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


    Операнды



    Операнды


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

    Обозначение = УточнИдент { "." идент | "[" СписокВыражений "]" | "^" | "(" УточнИдент ")" }.
    СписокВыражений = Выражение {"," Выражение}.

    Если а - обозначение массива, a[e] означает элемент а, чей индекс - текущее значение выражения e. Тип e должен быть целым типом. Обозначение вида a[e0, e1, ..., en] применимо вместо a[e0] [e1] ... [en]. Если r обозначает запись, то r.f означает поле f записи r или процедуру f, связанную с динамическим типом r (Гл. ). Если p обозначает указатель, p^ означает переменную, на которую ссылается p. Обозначения p^.f и p^[e] могут быть сокращены до p.f и p[e], то есть запись и индекс массива подразумевают разыменование. Если a или r доступны только для чтения, то a[e] и r.f также предназначены только для чтения.
    Охрана типа v(T) требует, чтобы динамическим типом v был T (или расширение T), то есть выполнение программы прерывается, если динамический тип v - не T (или расширение T). В пределах такого обозначения v воспринимается как имеющая статический тип T. Охрана применима, если
  • v - параметр-переменная типа запись, или v - указатель, и если
  • T - расширение статического типа v

  • Если обозначенный объект - константа или переменная, то обозначение ссылается на их текущее значение. Если он - процедура, то обозначение ссылается на эту процедуру, если только обозначение не сопровождается (возможно пустым) списком параметров. В последнем случае подразумевается активация процедуры и подстановка значения результата, полученного при ее исполнении. Фактические параметры должны соответствовать формальным параметрам как и при вызовах собственно процедуры (см. ).
    Примеры обозначений (со ссылками на примеры из Гл. 7):

    i (INTEGER)
    a[i] (REAL)
    w[3].name[i] (CHAR)
    t.left.right (Tree)
    t(CenterTree).subnode (Tree)



    Операторы Case



    Операторы Case


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

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

    Пример:
    CASE ch OF
    "A" .. "Z": ReadIdentifier
    | "0" .. "9": ReadNumber
    | "'", '"' : ReadString
    ELSE SpecialCharacter
    END


    Операторы For



    Операторы For


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

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

    Оператор
    FOR v := beg TO end BY step DO statements END
    эквивалентен
    temp := end; v := beg;
    IF step > 0 THEN
    WHILE v <= temp DO statements; v := v + step END
    ELSE
    WHILE v >= temp DO statements; v := v + step END
    END
    temp и v имеют одинаковый тип. Шаг (step) должен быть отличным от нуля константным выражением. Если шаг не указан, он предполагается равным 1.
    Примеры:
    FOR i := 0 TO 79 DO k := k + a[i] END
    FOR i := 79 TO 1 BY -1 DO a[i] := a[i-1] END


    Операторы If



    Операторы If


    ОператорIf =
    IF Выражение THEN ПоследовательностьОператоров
    {ELSIF Выражение THEN ПоследовательностьОператоров}
    [ELSE ПоследовательностьОператоров]
    END.
    Операторы if задают условное выполнение входящих в них последовательностей операторов. Логическое выражение, предшествующие последовательности операторов, будем называть условием (в оригинале - guard. Прим. перев.) Условия проверяются последовательно одно за другим, пока очередное не окажется равным TRUE, после чего выполняется связанная с этим условием последовательность операторов. Если ни одно условие не удовлетворено, выполняется последовательность операторов, записанная после слова ELSE, если оно имеется.
    Пример:
    IF (ch >= "A") & (ch <= "Z") THEN ReadIdentifier
    ELSIF (ch >= "0") & (ch <= "9") THEN ReadNumber
    ELSIF (ch = " ' ") OR (ch = ' " ') THEN ReadString
    ELSE SpecialCharacter
    END


    Операторы Loop



    Операторы Loop


    Оператор loop определяет повторное выполнение последовательности операторов. Он завершается после выполнения оператора выхода внутри этой последовательности (см. ).
    ОператорLoop = LOOP ПоследовательностьОператоров END.
    Пример:
    LOOP
    ReadInt(i);
    IF i < 0 THEN EXIT END;
    WriteInt(i)
    END
    Операторы loop полезны, чтобы выразить повторения с несколькими точками выхода, или в случаях, когда условие выхода находится в середине повторяемой последовательности операторов.


    Операторы Repeat



    Операторы Repeat


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


    Операторы возврата и выхода



    Операторы возврата и выхода


    Оператор возврата выполняет завершение процедуры. Он обозначается словом RETURN, за которым следует выражение, если процедура является процедурой-функцией. Тип выражения должен быть совместим по присваиванию (см. Приложение A) с типом результата, определенным в заголовке процедуры (см. Гл. 10).
    Процедуры-функции должны быть завершены оператором возврата, задающим значение результата. В собственно процедурах оператор возврата подразумевается в конце тела процедуры. Любой явный оператор появляется, следовательно, как дополнительная (вероятно, для исключительной ситуации) точка завершения.
    Оператор выхода обозначается словом EXIT. Он определяет завершение охватывающего оператора loop и продолжение выполнения программы с оператора, следующего за оператором loop. Оператор выхода связан с содержащим его оператором loop контекстуально, а не синтаксически.


    Операторы While



    Операторы While


    Операторы while задают повторное выполнение последовательности операторов, пока логическое выражение (условие) остается равным TRUE. Условие проверяется перед каждым выполнением последовательности операторов.
    ОператорWhile = WHILE Выражение DO ПоследовательностьОператоров END.
    Примеры:
    WHILE i > 0 DO i := i DIV 2; k := k + 1 END
    WHILE (t # NIL) & (t.key # i) DO t := t.left END


    Операторы With



    Операторы With


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

    ОператорWith = WITH Охрана DO ПоследовательностьОператоров {"|" Охрана DO ПоследовательностьОператоров} [ELSE ПоследовательностьОператоров] END.
    Охрана = УточнИдент ":" УточнИдент.

    Если v - параметр-переменная типа запись или переменная-указатель, и если ее статический тип T0, оператор
    WITH v: T1 DO S1 | v: T2 DO S2 ELSE S3 END
    имеет следующий смысл: если динамический тип v - T1, то выполняется последовательность операторов S1 в которой v воспринимается так, будто она имеет статический тип T1; иначе, если динамический тип v - T2, выполняется S2, где v воспринимается как имеющая статический тип T2; иначе выполняется S3. T1 и T2 должны быть расширениями T0. Если ни одна проверка типа не удовлетворена, а ELSE отсутствует, программа прерывается.
    Пример:
    WITH t: CenterTree DO i := t.width; c := t.subnode END


    Операторы



    9. Операторы


    Операторы обозначают действия. Есть простые и структурные операторы. Простые операторы не содержат в себе никаких частей, которые являются самостоятельными операторами. Простые операторы - присваивание, вызов процедуры, операторы возврата и выхода. Структурные операторы состоят из частей, которые являются самостоятельными операторами. Они используются, чтобы выразить последовательное и условное, выборочное и повторное исполнение. Оператор также может быть пустым, в этом случае он не обозначает никакого действия. Пустой оператор добавлен, чтобы упростить правила пунктуации в последовательности операторов.
    Оператор =
    [Присваивание | ВызовПроцедуры
    | ОператорIf | ОператорCase | ОператорWhile | ОператорRepeat
    | ОператорFor | ОператорLoop | ОператорWith
    | EXIT | RETURN [Выражение]].


    Основные типы



    Основные типы


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

    1. BOOLEAN логические значения TRUE и FALSE
    2. CHAR символы расширенного набора ASCII (0X .. 0FFX)
    3. SHORTINT целые в интервале от MIN(SHORTINT) до MAX(SHORTINT)
    4. INTEGER целые в интервале от MIN(INTEGER) до MAX(INTEGER)
    5. LONGINT целые в интервале от MIN(LONGINT) до MAX(LONGINT)
    6. REAL вещественные числа в интервале от MIN(REAL) до MAX(REAL)
    7. LONGREAL вещественные числа от MIN(LONGREAL) до MAX(LONGREAL)
    8. SET множество из целых от 0 до MAX(SET)

    Типы от 3 до 5 - целые типы, типы 6 и 7 - вещественные типы, а вместе они называются числовыми типами. Эти типы образуют иерархию; больший тип поглощает меньший тип:
    LONGREAL >= REAL >= LONGINT >= INTEGER >= SHORTINT


    От переводчика



    От переводчика


    Язык программирования Оберон создан автором Паскаля и Модулы-2 Никлаусом Виртом в 1987 году в ходе разработки одноименной операционной системы для однопользовательской рабочей станции Ceres. Язык и операционная система названы именем одного из спутников планеты Уран - Оберона, открытого английским астрономом Уильямом Гершелем ровно за двести лет до описываемых событий.
    "Сделай так просто, как возможно, но не проще того" - это высказывание А.Эйнштейна Вирт выбрал эпиграфом к описанию языка. Удивительно простой и даже аскетичный Оберон является, вероятно, минимальным универсальным языком высокого уровня. Он проще Паскаля и Модулы-2 и в то же время обогащает их рядом новых возможностей. Важно то, что автором языка руководили не сиюминутные коммерческие и конъюнктурные соображения, а глубокий анализ реальных программистских потребностей и стремление удовлетворить их простым, понятным, эффективным и безопасным образом, не вводя по возможности новых понятий.
    Являясь объектно-ориентированным языком, Оберон даже не содержит слова object. Оберон представляется идеальным языком для изучения программирования. Сочетание простоты, строгости и неизбыточности предоставляет начинающему программисту великолепную возможность, не заблудившись в дебрях, выработать хороший стиль, освоив при этом и структурное и объектно-ориентированное и модульно-компонентное программирование.
    В 1992 году сотрудничество Н.Вирта с привело к добавлению в язык ряда новых средств. Новая версия получила название Оберон-2. Описание именно этого языка по состоянию на 1 октября 1996 года (последние изменения внесены авторами в июле 1996 года) и дается в настоящем переводе. Оберон-2 представляет собой почти правильное расширение Оберона и является фактическим стандартом языка, который поддерживается большинством современных Оберон-систем. В Оберон-2 добавлены:
  • связанные с типом процедуры;
  • экспорт только для чтения;
  • открытые массивы в качестве базового типа для указателей;
  • оператор with с вариантами;
  • оператор for.


  • Отдельного внимания заслуживает само описание, с которым вам предстоит познакомиться. Вирт и его соавтор достигли совершенства не только в искусстве разработки, но, несомненно, и в деле описания языков программирования. Поражают изумительная точность и краткость этого документа. Почти каждая его фраза превращается при написании компилятора в конкретные строки программного кода.
    Возникшие при переводе описания Оберона-2 на русский язык терминологические вопросы решались исходя из следующих соображений: предпочтительным является буквальный перевод; недопустимо добавление терминов, отсутствующих в оригинале; должны быть учтены отечественные традиции в терминологии алголоподобных языков; предпочтительно использование терминов, привычных широкому кругу программистов, вместо узкоспециальных. Ниже приведен список терминов, перевод которых не представляется очевидным.

    (direct) base type (непосредственный) базовый тип
    array compatible совместимый массив
    array type тип массив
    assignment compatible совместимый по присваиванию
    basic type основной тип
    browser смотритель
    case statement оператор case
    character символ, знак
    declaration объявление
    designator обозначение
    direct extension непосредственное расширение
    equal types равные типы
    exit statement оператор выхода
    expression compatible совместимое выражение
    for statement оператор for
    function procedure процедура-функция
    if statement оператор if
    loop statement оператор loop
    matching совпадение
    operator операция
    pointer type тип указатель
    predeclared стандартный
    private field скрытое поле
    proper procedure собственно процедура
    public field доступное поле
    qualified уточненный
    real вещественный
    record type тип запись
    repeat statement оператор repeat
    return statement оператор возврата
    same type одинаковый тип
    scale factor порядок
    scope область действия
    statement оператор
    string строка
    symbol слово
    type extension расширение типа
    type guard охрана типа
    type inclusion поглощение типа
    type tag тег
    type test проверка типа
    type-bound procedures связанные с типом процедуры
    while statement оператор while
    with statement оператор with

    с3с@uni-vologda.ac.ru
    2 октября 1996 г. - 12 июня 1998 г.
    Вологда


    Отношения



    Отношения



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

    Отношения дают результат типа BOOLEAN. Отношения =, #, <, <=, >, и >= применимы к числовым типам, типу CHAR, строкам и символьным массивам, содержащим в конце 0X. Отношения = и # кроме того применимы к типам BOOLEAN и SET, а также к указателям и процедурным типам (включая значение NIL). x IN s означает "x является элементом s". x должен быть целого типа, а s - типа SET. v IS T означает "динамический тип v есть T (или расширение T)" и называется проверкой типа. Проверка типа применима, если
  • v - параметр-переменная типа запись, или v - указатель, и если
  • T - расширение статического типа v

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

    1991 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
    w[i].name <= "John" BOOLEAN
    t IS CenterTree BOOLEAN



    Поглощение типов



    Поглощение типов


    Числовые типы поглощают (значения) меньших числовых типов согласно следующей иерархии:
    LONGREAL >= REAL >= LONGINT >= INTEGER >= SHORTINT


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



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


    Последовательность операторов, разделенных точкой с запятой, означает поочередное выполнение действий, заданных составляющими операторами.
    ПоследовательностьОператоров = Оператор {";" Оператор}.


    A: Определение терминов



    Приложение A: Определение терминов



    Целые типы SHORTINT, INTEGER, LONGINT
    Вещественные типы REAL, LONGREAL
    Числовые типы Целые типы, вещественные типы



    WHILE Выраж DO ПослОператоров END



    Приложение B: Синтаксис Оберона-2

    Модуль = MODULE идент ";" [СписокИмпорта] ПослОбъявл [BEGIN ПослОператоров] END идент ".".
    СписокИмпорта = IMPORT [идент ":="] идент {"," [идент ":="] идент} ";".
    ПослОбъявл = { CONST {ОбъявлКонст ";" } | TYPE {ОбъявлТипа ";" } | VAR {ОбъявлПерем ";" }} {ОбъявлПроц ";" | ОпережающееОбъяв";"}.
    ОбъявлКонст = ИдентОпр "=" КонстВыраж.
    ОбъявлТипа = ИдентОпр "=" Тип.
    ОбъявлПерем = СписокИдент ":" Тип.
    ОбъявлПроц = PROCEDURE [Приемник] ИдентОпр [ФормальныеПарам]";" ПослОбъявл [BEGIN ПослОператоров] END идент.
    ОпережающееОбъяв = PROCEDURE "^" [Приемник] ИдентОпр [ФормальныеПарам].
    ФормальныеПарам = "(" [СекцияФП {";" СекцияФП}] ")" [":" УточнИдент].
    СекцияФП = [VAR] идент {"," идент} ":" Тип.
    Приемник = "(" [VAR] идент ":" идент ")".
    Тип = УточнИдент | ARRAY [КонстВыраж {"," КонстВыраж}] OF Тип | RECORD ["("УточнИдент")"] СписокПолей {";" СписокПолей} END | POINTER TO Тип | PROCEDURE [ФормальныеПарам].
    СписокПолей = [СписокИдент ":" Тип].
    ПослОператоров = Оператор {";" Оператор}.
    Оператор = [ Обозначение ":=" Выраж
    | Обозначение ["(" [СписокВыраж] ")"]
    | IF Выраж THEN ПослОператоров {ELSIF Выраж THEN ПослОператоров} [ELSE ПослОператоров] END
    | CASE Выраж OF Вариант {"|" Вариант} [ELSE ПослОператоров] END
    | WHILE Выраж DO ПослОператоров END
    | REPEAT ПослОператоров UNTIL Выраж
    | FOR идент ":=" Выраж TO Выраж [BY КонстВыраж] DO ПослОператоров END
    | LOOP ПослОператоров END
    | WITH Охрана DO ПослОператоров {"|" Охрана DO ПослОператоров}
    [ELSE ПослОператоров] END
    | EXIT
    | RETURN [Выраж] ].
    Вариант = [МеткиВарианта {"," МеткиВарианта} ":" ПослОператоров].
    МеткиВарианта = КонстВыраж [".." КонстВыраж].
    Охрана = УточнИдент ":" УточнИдент.
    КонстВыраж = Выраж.
    Выраж = ПростоеВыраж [Отношение ПростоеВыраж].
    ПростоеВыраж = ["+" | "-"] Слагаемое {ОперСлож Слагаемое}.
    Слагаемое = Множитель {ОперУмн Множитель}.
    Множитель = Обозначение ["(" [СписокВыраж] ")"] | число | символ | строка | NIL | Множество | "(" Выраж ")" | " ~ " Множитель.
    Множество = "{" [Элемент {"," Элемент}] "}".
    Элемент = Выраж [".." Выраж].
    Отношение = "=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS.
    ОперСлож = "+" | "-" | OR.
    ОперУмн = "*" | "/" | DIV | MOD | "&".
    Обозначение = УточнИдент {"." идент | "[" СписокВыраж "]" | "^" | "(" УточнИдент ")"}.
    СписокВыраж = Выраж {"," Выраж}. СписокИдент = ИдентОпр {"," ИдентОпр}.
    УточнИдент = [идент "."] идент. ИдентОпр = идент [ "*" | "-" ].

    C: Модуль SYSTEM