lang_abstract.md 72 KB

Абстракция языка Оберон-У

Абстракция языка Оберон-У (Оберон-Умелый, Oberon-Deft)

Ревизия: v0.0.2 / 2023-08-01

Все должно быть сделано максимально просто, но не примитивно.
(А. Эйнштейн)

Оглавление

0. Введение

Идея создания языка Оберон-У (далее просто Оберон) родилась после переосмысления идей несоответствия современного ИТ и средств разработки. Никлаус Вирт продвинулся в этом отношении дальше всех и львиная доля идей в концепции Оберон является либо трансляцией этих идей, либо творческой компиляции их.

Оберон-У — язык программирования общего назначения, который является развитием Модулы-2 и модификацией Оберон-7.

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

Назначение этого документа в том, чтобы служить эталоном для программистов, разработчиков и авторов руководств. Если о чём-то не сказано, то обычно это ошибка.

Этот документ описывает язык как достаточный для реализации различного рода ПО: начиная от загрузчиков ОС, сами ОС, системное и прикладное ПО.

Чтобы лучше понимать идею создания языка рекомендуется ознакомиться с некоторыми концепциями построения бесшовной системной среды.

1. Синтаксис

Для описания синтаксиса используется диалект расширенной формы Бэкуса—Наура (РБНФ) -- НШ (здесь краткая справка). Литера вопроса ? означают необязательность записанного внутри них выражения, а двоеточие .. означают его повторение (минимум 1 раз).

Законченный текст на языке программирования Оберон называется программа.

Программа включает как минимум один модуль. В программе как правило модулей больше одного, количество модулей в программе не ограничено. Каждый модуль должен обладать правильной структурой:

Модуль имеет строго заданную структуру:

модуль Старт;

Импорты?, Типы?, Константы?, Переменные?, Функции?;

{
    Выражения?; // При загрузке модуля
}

До начала модуля и после окончания модуля может быть любой текст.

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

  • пользовательский символ;
  • встроенный символ;

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

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

2. Синтез символов

Для составоения символов используются литеры. В группу литеры входят несколько подгрупп:

  • Буква;
  • Цифра;
  • Разделитель;
  • СлужебнаяЛитера;

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

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

Имя = Буква + (Буква..? | Цифра..?);

Примеры:

x Сканер Оберон Символ_Получить перваяЛитера name2

В группу Число входят символы:

  • ЧислоЦелое;
  • ЧислоДробное.

ЧислоЦелое являются последовательностью Цифра и могут быть продолжены литерой суффикса H слитно в конце числа.

  • Без суффикса -- представление десятичное;
  • Суффикс H -- представление шестнадцатеричное.

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

Цифра   = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
ЦифраШестн = Цифра | "A" | "B" | "C" | "D" | "E" | "F";

ЧислоЗнак = "+"? | "-"?;
ЧислоЦелое   = ЧислоЗнак? Цифра | Цифра..? ЦифраШестн? + "H"?;
ЧислоДробное = ЧислоЗнак? Цифра | Цифра..? + "." + Цифра..Порядок?;
Порядок = "E" ЧислоЗнак? Цифра | Цифра..?;

Число   = ЧислоЦелое | ЧислоДробное.

Примеры:

1987 100H = 256 12.3 4.567E8 = 456700000

Строковые литералы — последовательность литер, заключенных в одиночные кавычки '. Ограничивающая кавычка не должна встречаться внутри литерала, но допускается:

  • внутри литерала может быть кавычка с экраном;
  • также непечатаемые символы должны быть экранированы даун-слэшем.

Число литер в литерале называется длиной строкового литерала.

Строка = "'" + (Литера..? | Цифра..?)? + "'";

Примеры:

'ОБЕРОН' 'Don\'t worry!' 'Hey, "world"!'

Все символы делятся на две группы:

  • встроенные символы (использованы в языке);
  • пользовательские символы (определяются пользователем).

В группу встроенных символов входят:

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

Ключевые символы являются специальной группой и они не могут использоваться в качестве пользовательских символов ни в какой части программы.

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

Разделитель включает в себя:

  • пробел;
  • табуляция;
  • перенос строки.

Для более полного понимания зависимостей ниже приводится структурная схема:

flowchart TD
    Программа
    Модуль0
    Модуль1
    Символ0
    Символ1
    Пользовательские_символы
    Встроенные_символы
    Имя
    Литеры
    Программа --> Модуль0
    Программа --> Модуль1
    Модуль1 --> Символ0
    Модуль1 --> Символ1
    Символ1 --> Пользовательские_символы
    Символ1 --> Встроенные_символы
    Встроенные_символы --> Ключевые_символы
    Встроенные_символы --> Предписания
    Встроенные_символы --> Операторы
    Встроенные_символы --> Разделители
    Встроенные_символы --> Комментарии
    Пользовательские_символы --> Имя
    Ключевые_символы --> Имя
    Предписания --> Имя
    Имя --> Литеры
    Операторы --> Литеры
    Разделители --> Литеры
    Комментарии --> Литеры
    Литеры --> Буквы
    Литеры --> Цифры
    Литеры --> Служебные
    Цифры --> Целые
    Цифры --> Дробные

3. Ключевые символы

Ниже перечислен полный список ключевых символов

Символ Комментарий
+ Сложение
- Минус
* Умножение
/ Деление
= Присвоение
< Меньше
<= Меньше или равно
> Больше
>= Больше или равно
// Линейный комментарий
/* Начало многострочного комментария
*/ Конец многострочного комментария
. Точка
, Запятая
; Разделитель выражений
.. Конструктор диапазона
: Определить тип переменной
( Открывающая круглая скобка (выражение)
) Закрывающая круглая скобка (выражение)
[ Открывающая квадратная скобка (массив)
] Закрывающая квадратная скобка (массив)
{ Фигурная открывающая скобка (область видимости)
} Закрывающая фигурная скобка (область видимости)
модуль MODULE Объявление начала модуля
импорт IMPORT Импорт модуля
конст CONST Объявление константы
тип TYPE Объявление типа
бул BOOL Белвый тип
да TRUE Булево значение
нет FALSE Отрицание логического значения
байт BYTE Тип байт
целое INT Дробное число
вещ REAL Дробное число
массив ARRAY Массив элементов
структ STRUCT Объявление структуры
абстракт ABSTRACT Объявление абстрактной структуры
симв VAR Объявление переменной
мут MUT Признак изменяемости
пуб PUB Признак экспорта
фн FN Объявление функции
& & Создание объекта
есть IS Проверка типа
если IF Проверка условия
провер SWITCH Проверка на выбор
ветка CASE ветка выбора
вернуть RETURN Возврат из функции
нич NIL Пустое состояние объекта
цикл LOOP Безусловный цикл
дальше NEXT Продолжить цикл с пропуском следующих выражений
стоп BREAK Прервать цикл с пропуском следующих выражений
или OR Логическое или
и AND Логическое и
не NOT Логическая инверсия

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

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

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

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

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

ИмяКвал = имя + "." + имя; ИмяЭкспорт = "пуб" + имя; ИмяИзмен = "мут + имя; ИмяСимвол = ИмяЭкспорт? ИмяИзмен? имя;

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

ASR
ASSERT
бул
байт
EXCL
FLT
целое
LSL
нов
ODD
ORD
PACK
вещ
ROR
SET
UNPK

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

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

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

Константное выражение может быть вычислено по его тексту на этапе компиляции программы. Его операнды — константы. Значение константы не изменяется никогда. Примеры объявлений констант:

конст {
    число = 100,
    лимит = 2 * число - 1,
    словаВсе = [0 .. словаРазмер -1],
    имя = 'Oberon',
}

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

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

Типы данных деляется на две группы:

  • встроенные типы;
  • пользовательские типы;

В группе встроенных типов также есть деление на две части:

  • линейный типы;
  • векторные типы.

Примеры:

тип {
    Таблица = массив[255]вещ,

    структ Узел {
        ключ: целое,
        Левый, Правый: Дерево,
    }

    Дерево = Узел,

    структ УзелСередина (Дерево){
        Имя: массив[32]байт,
        Подузел: Узел,
    }

    ДеревоСередина = УзелСередина,

    Функция = ФН (x: целое): целое,
}

6.1 Встроенные линейные типы

Встроенные линейные типы представлены ниже:

Встроенные линейные типы:

  • бул -- принимает значения да и нет;
  • байт -- целые числа от 0 до 255, размер фиксированный в 8 бит;
  • целое -- целые числа, размер зависит от аппаратуры;
  • вещ -- дробные числа, размер зависит от аппаратуры;

Тип байт совместим с типом целое, но наоборот требуется приведение. Тип целое совместим с типом вещ, но наоборот требуется приведение. В любом случае приведение всегда выполняется явно.

6.2 Встроенные векторные типы

В группе векторных типов есть два типа:

  • массив;
  • литеральная строка.

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

ТипМассива = массив[длина, длина..?]ТипИмя;
длина = Выражение | КонстантноеВыражение;

Форма объявления

массив[N0, N1, ..., Nk]T

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

симв {
    listUser = массив[NN]целое,
    moments = массив[10,20]вещ,
}

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

Пример:

симв {
    strHello = `Привет`,
}

После присвоения литеральная строка представлена массивом байтов.

ОбъявлениеТипа = ИмяТип + "=" + СТРУКТ (ИмяТип?) { Поле..? };
Тип = ИмяКвал | ТипСущность | ТипМассив | ТипСтруктура | ТипФункция;

6.3 Структура

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

ТипЗаписи = структ (БазовыйТип?) {
    СписокПолейТипа
};
БазовыйТип = квалификатор;
СписокПолейТипа= Поле..?;
Поле = Имя, Имя..? + ":" + Тип;

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

Определение. Тип T1 является T0, если он непосредственно расширяет T0. и наоборот, тип T0 является базовым типом для типа T1, если он является прямым базовым типом базового типа T1. Примеры типов структур:

тип {
    Дата = структ {
        день, месяц, год: целое,
    }
    User = структ (Дата){
        имя, фамилия: массив[32]байт,
        возраст: целое,
        зарплата: вещ,
    }
}

6.4 Абстрактные т=структуры

Структуры могут быть объявлены с квалификатором абстракт. Подобные структуры могут обладать сигнатурами методов, но не полями или самими методами.

Абстрактные структуры можно встраивать в структуры. Поскольку полей в них нет, производная структура также не содержит полей (но можно добавить), а методы абстрактной структуры в производной должны быть обязательно описаны. Если этого не сделать, то будет ошибка во время компиляции.

Абстрактные структуры могут встраивать абстрактные структуры. Если есть две абстрактные структуры с одинаковым набором методов, но разным именем -- это разные структуры.

Создать объект абстрактной структуры нельзя. Абстрактную структуру можно только реализовать.

СтруктураАбстракт = Имя + "=" СТРУКТ АБСТРАКТ {
    СписокМетодов..?;
}

Пример:

тип {
    База = структ абстракт {
        Открыть([]байт)ошибка;
        Закрыть()ошибка;
    }
    БазаРасшир = структ абстракт (База) {
        Строить()ошибка;
        Шахта()уголь;
    }
}

сущ {
    базаА, базаБ = База;
    базаВ = БазаРасшир;
}

7 Тип функции

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

Все переменные присваиваются ссылкой на функцию.

ТипФункция = ФН (ФормальныеПараметры?)Результат?;

8. Объявление объектов

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

ОбъявлениеОбъектов = Имя, Имя..? ":" тип;

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

тип {
    Текст = структ {
        лит: байт,
        счётчик: целое,
    }
}

симв {
    i, j, k: целое,
    x, y: вещ,
    p, q: бул,
    f: ФН(),
    a: массив[100]вещ,
    w: массив[16]Текст,
    t: Дерево,
}

9. Выражения

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

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

9.1 Операнды

За исключением чисел и литерных констант, операнды обозначаются именами. Обозначение операнда состоит из имени, которое может оносится к символам:

  • константа;
  • объект;
  • функция;
  • тип;

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

СелекторСимвола = Модуль? + "."? +  Имя | "["СписокВыражения"]";
СписокВыражения = выражение..?;

Если обозначенное имя символа является объектом, то имя объекта ссылается на текущее значение самого объекта.

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

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

i              (целое)
а[I]           (вещ)
w[3].ch        (байт)
t.ключ         (целое)
t.левый.правый (Дерево)
t(УзелСредний).подузел(Дерево)

9.2 Операторы

Синтаксис выражений различает четыре вида операторов с разными приоритетами (порядком выполнения).

  • *, / (умножение и деление)
  • +,- (сложения и вычитание)

Операции одного и того же приоритета выполняются слева направо. Например,

x-y-z

означает

(x-y)-z

Операция = ОперацияПростая | ОперацияОтношение;
ОперацияОтношение = "=" | "<" | "<=" | ">" | ">=" | есть;
ОперацияПростая = "+" | "−"  Разделитель? ОператорАддикции Разделитель?;
ОперациярАддикции= "+" | "−" | или;
Разделитель = пробел.. | табуляция.. | ПереводСтроки..;
ОператорМультипликации = "*" | "/" | и;
Множитель = число| строка| ПУСТО | да | нет | ФактическийПараметр.. | "(" выражение ")" | "~" множитель;
ОперацияВыборки = выражение выражение..?;
ФактическийПараметр = (СписокВыражения..?);

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

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

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

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

р или q // означает «если р, то да, иначе q» p и q // обозначает «если р, то q, иначе нет не P // означает "не p"

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

Символ Результат
+ сложение
- вычитание
* умножение
/ деление

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

  • - обозначает инверсию знака;
  • + обозначает оператор идентичности.

9.2.3 Отношения

Символ Результат
== равно
!= неравно
< меньше
<= меньше или равно
> больше
>= больше или равно
есть проверка типа

Результат операций отношения является базовым типом бул. ОперацияОтношение (==,!=, <, <=,>,>=) применяется к числовым типам и байт. Отношения == и != применимы также к типам бул, и функциям.

v есть T означает v ЯВЛЯЕТСЯ типом T и вызывает проверку типа. Это применимо, если выполнены все условия:

  • T расширение объявленной структуры T0 для v;
  • v параметр, как объект заданного типа структуры.

Предполагая, например, что тип структуры T является расширением базовой структуры T0, тогда проверка v есть T определяет, является ли фактически указанный объект (в том числе, а не только T0) cтруктурой типа T. Значение ПУСТО есть Т возвращает отрицательный результат. Примеры выражений (см. Примеры в главе 7):

1987                (`целое`)
P == q              (`бул`)
(I + j) * (i-j)     (`целое`)
A [i + j] * a [i-j] (`вещ`)
(0 <= i) и (i <100) (`бул`)
T.ключ == 0         (`бул`)
K == i              (`бул`)
T есть УзелСредний  (`бул`)

10. Предписания

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

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

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

Предписание = Оператор | Присвоение | ВызовПроцедур | Предписание_ЕСЛИ |Предписание_ВЫБОР | Предписание_ЦИКЛ.

10.1 Предписание ПРИСВОЕНИЕ

Элементарное предписание ПРИСВОЕНИЕ служит для замены текущего значения объекта на новое значение, заданное выражением. Предписание ПРИСВОЕНИЕ записывается как «=» и произносится как «присвоить».

присвоение = объект + "=" + выражение;

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

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

  • константу ПУСТО можно присвоить объектам любого типа;
  • литеральные строки могут быть назначены любому массиву байтов длины больше, чем длина литеральной строки +1. В массив будет добавлена в конец литера с кодом ноль. Однолитерные строки также могут быть присвоены объектам типа байт;
  • в случае структур тип источника должен быть типом структуры получателя,
  • массив может быть назначен другому массиву равного типа.

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

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

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

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

Последовательность_Предписаний = Предписание? ; Предписание..?;

10.4 Предписание_ЕСЛИ

Предписание если проверяет булево выражение. Если булево выражение положительное -- далее выполняется последовательность предписаний.

ПредписаниеЕсли = ЕСЛИ ВыражениеБул {
    СписокВыражений..?;
}

Пример:

если Х==0{
    Печать(`Вот и результат!`);
}

10.5 Предписание_ВЫБОР

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

Предписание_ВЫБОР= выбор выражение СписокМеток_ВЫБОР;
Выбор = Сущность ":" СписокВыражений;
СписокМеток_ВЫБОР = МеткиДляВыбора["," МеткиДляВыбора].
МеткиДляВыбора = Метка .. Метка..?;
Метка = целое | строка | квалификатор.

Пример:

выбор k {
    провер 0: x = x + y,
    провер 1: x = x − y,
    провер 2: x = x * y,
    провер 3: x = x / y,
}

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

Пример:

тип {
    СвязьЦел = структ {
        a : целое,
    }
    СвязьВещ = структ{
        а:вещ,
    }
}

сущ {
    р0 = СвязьЦел {
        а:5,
    }
    р1 = СвязьВещ {
        а:6.1,
    }
    P = тЗапись{}
}

{
    P0 = R0;
    P1 = R1;
    P2 = R2;

    выбор p {
        провер P0: p.а = 10,
        провер P1: p.а = 2.5,
        провер P2: p.а = 0,
    }
}

10.7 Предписание цикл

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

Предписание_ЦИКЛ =
цикл {
    ПоследовательностьПредписаний..?;
}

11. Объявление функций

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

Существует два типа функций, а именно функции и функции-процедуры.

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

Функции активируются вызовом функции. Функция отличается от функции-процедуры в объявлении путём указания типа её результата после списка параметров. Её тело должно заканчиваться ключевым словом вернуть, которое определяет результат функции.

Все константы, объекты и типы, объявленные в теле функции, являются локальными для этой функции. Значения локальных объектов не определено при входе в функцию.

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

ОбъявлениеФункции = ИмяФункции ТелоФункции;
ИмяФункции = фн ИмяФункции(ФормальныеПараметры..)Результат?;
ТелоФункции = ПоследОбъявлений..? ПоследПредписаний..? вернуть Выражение;
ПоследОбъявлений = ОбъявлениеКонстант..? ; ОбъявлениеТипов..? ; ОбъявлениеОбъектов?;

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

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

Тип параметра указывается в списке формальных параметров.

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

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

Формальныйпараметр = "(" + Параметр..? + ")";
Параметр = (Имя, Имя..? + ":" + Тип + ";"?)..?

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

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

Примеры объявлений функций:

фн Целое_Читать(симв x: целое) {
    симв {
        i : целое,
        ch: байт,
    }
    i = 0;
    Читать(ch);
    пока (`0` <= ch) и (ch <= `9`) {
        i = 10*i + (байт(ch)-байт(`0`));
        Читать(ch);
    }
    x = i
}

фн Целое_Писать(x: целое) { // 0 <= x < 10^5
    симв {
        i: целое,
        buf: массив[5]целое,
    }
    i = 0;
    петля {
    если i==0 {
        вернуть;
    }
    buf[i] = x \ 10;
    x = x * 10;
    i = i + 1;
    Записать(байт(buf[i] + байт(`0`)));
    }
}

фн лог2(x: целое):целое{
    симв {
        y: целое, // assume x>0
    }
    у = 0;
    цикл {
        если х > 1 {
            стоп;
        }
        x = x \ 2;
        y = у + 1;
    }
    вернуть y;
}

11.2. Встроенные функции

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

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

Имя Тип аргумента Тип результата Описание Англ
м.Длин() v: массив целое длина массива v v.Len()

Функции преобразования типов:

Имя Тип аргумента Тип результата Функция Англ
Вещ вещ целое округление вниз FLOOR(x)
Целое целое вещ преобразование типа FLT(x)
Байт байт целое порядковый номер x ORD(x)

Безопасные процедуры:

Имя Тип аргумента Функция Англ
?v переменная размещение переменной в памяти ?v
контр BOOLEAN прервать, если нет ASSERT(b)

Функция Округ(x) дает наибольшее целое число, не большее x.

Округ(1.5) = 1
Округ(-1.5) = -2

12. Модули

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

Модуль = модуль Имя;
    СписокИмпорта..?  СписокОбъявлений..? СписокФункций..?
{
    СписокВыражений..?;
}

СписокИмпорта = импорт {
    ИмпортМодуля..?
}

ИмпортМодуля = Имя + "=" + МодульПуть;

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

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

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

Пример:

модуль Вывод; // экспортирует процедуры: Write, WriteInt, WriteLn

импорт {
    Текст = `./text`,
    Oberon = `./oberon/compiler`,
}

симв {
    W: Текст.Writer,
}

фн Write*(ch: байт){
    Текст.Write(W, ch);
}

фн WriteInt*(x, n: `целое`){
    симв {
        i: целое,
        a: массив[16]байт,
    }
    i = 0;
    если  i == 0; {
        Текст.Текст(W, "-");
        x = -x;
    }
    цикл {
        если х == 0 {
            стоп;
        }
        a[i] = Вещ(x MOD 10 + Байт(`0`));
        x = x \ 10;
        i = i + 1;
    }
    Текст.Write(W, " ");
    n = n - 1;
    еслиХ = ;
    цикл {
        если n > 1 {
            стоп;
        }
        i = i - 1;
    }
    Текст.Write(W, a[i]);
}

фн WriteLn*{
    Текст.WriteLn(W);
    Текст.Append(Oberon.Log, W.buf);
}

{
    Текст.OpenWriter(W)
}

12.1 Модуль СИСТЕМА

Необязательный модуль СИСТЕМА содержит определения, необходимые для программирования опасных операций, ссылающихся непосредственно на ресурсы, специфичные для данной аппаратуры.

Импорт модуля СИСТЕМА может быть пустым для обозначения опасных операций или прежупреждения о неисправном коде или устервшем коде.

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

В модуле СИСТЕМА есть две причины для упрощения процедур:

  • их значение зависит от реализации, то есть значение не выводится из определения языка;
  • они могут повредить систему (например, Запись). Настоятельно рекомендуется ограничить их использование конкретными низкоуровневыми модулями, поскольку такие модули по своей сути являются не переносимыми, и не «безопасными по типу». Однако они легко распознаются из-за идентификатора СИСТЕМА, появляющегося в списках импорта модуля.

Следующие определения обычно применимы без дополнительных изменений. Однако отдельные реализации языка могут включать в свои модули дополнительные определения СИСТЕМА, которые относятся к конкретной, находящейся в использовании аппаратуре. В дальнейшем v обозначает переменную, x, a и n для выражений.

Функции-процедуры:

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

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

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

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

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

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

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

Имя Тип аргумента Функция
LED(n) целое отображает n на LED-экране

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

литера = "A" | "B" | … | "Z" | "a" | "b" | … | "z".
цифра  "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".
шестнЦифра = digit | "A" | "B" | "C" | "D" | "E" | "F".
имя = литера {литера | цифра}.
квалификатор = [имя "."] имя.
ОпрИмени = имя ["*"].
целое = цифра {цифра} | цифра {шестнЦифра} "H".
вещественное = цифра{цифра} "." {цифра} [экспонента].
экспонента = "E" ["+" | "-"] цифра{цифра}.
число = целое | вещественное.
строка = """ {литера} """ | цифра {шестнЦифра } "X".
ОпрКонстанты = ОпрКонстанты "=" КонстВыражение.
КонстВыражение = выражение.
ОпрТипа = ОпрИмени "=" тип.
тип = квалификатор| ТипМассива | ТипЗаписи | ТипСсылка | ТипПроцедуры.
ТипМассива = массив длина {"," длина} OF тип.
длина = КонстВыражение .
ТипЗаписи = RECORD ["(" БазовыйТип ")"] [ПоследСписокПолей] END.
БазовыйТип = квалификатор.
ПоследСписокПолей = СписокПолей {";" СписокПолей }.
СписокПолей = СписокИмён ":" тип.
СписокИмён = ОпрИмени{"," ОпрИмени}.
ТипСсылка = POINTER TO тип.
ТипПроцедуры = PROCEDURE [ФормальныеПараметры].
ОпрПеременных = СписокИмён ":" тип.
выражение = ОперацияПростая [ОперацияОтношение ОперацияПростая ].
ОперацияОтношение = "=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS.
ОперацияПростая = ["+" | "-"] разделитель {ОперАддикции разделитель }.
ОперАддикции = "+" | "-" | OR.
разделитель = множитель {ОперМультипликации множитель}.
ОперМультипликации = "*" | "/" | DIV | MOD | "&".
множитель = число | строка | NIL | TRUE | FALSE | set | ссылка [АктуальныеПараметры] | "(" выражение")" | "~" множитель.
ссылка = квалификатор {селектор}.
селектор = "." имя| "[" СписокВыражений"]" | "^" | "(" квалификатор")".
набор = "{" [элемент {"," элемент}] "}".
элемент = выражение [".." выражение ].
СписокВыражений = выражение {"," выражение }.
АктуальныеПараметры = "(" [СписокВыражений ] ")" .
предписание = [присвоение | ВызовПроцедуры| Предписание_If | Предписание_Case |
Предписание_While| Предписание_Repeat | Предписание_For].

присвоение = ссылка ":=" выражение.
ВызовПроцедуры = ссылка [АктуальныеПараметры].
ПоследПредписаний = предписание {";" предписание }.
Предписание_If = IF выражение THEN ПоследПредписаний
{ELSIF выражение THEN ПоследПредписаний }
[ELSE ПоследПредписаний ] END.
Предписание_Case = CASE выражение OF вариант {"|" вариант } END.
вариант = [СписокМеток_Case ":" ПоследПредписаний ].
СписокМеток_Case = СписокМеток {"," СписокМеток }.
СписокМеток = метка [".." метка ].
метка = целое | строка | квалификатор .
Предписание_While = WHILE выражение DO ПоследПредписаний
{ELSIF выражение DO ПоследПредписаний } END.
Предписание_Repeat = REPEAT ПоследПредписаний UNTIL выражение .

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

ОпрПроцедуры = ЗаголовокПроцедуры ";" ТелоПроцедуры имя.
ЗаголовокПроцедуры = PROCEDURE ОпрИмени [ФормальныеПараметры].

ТелоПроцедуры = ПоследОпределений[BEGIN ПоследПредписаний ]
[RETURN выражение ] END.

ПоследОпределений = [конст {ОпрКонстант ";"}]
[TYPE {ОпрТипа ";"}]
[симв {ОпрПеременной ";"}]
{ОпрПроцедуры ";"}.
ФормальныеПараметры = "(" [Секция_FP {";" Секция_FP}] ")" [":" квалификатор].
Секция_FP = [симв] имя {"," имя } ":" ФормальныйТип.
ФормальныйТип = {массив OF} квалификатор.

модуль = MODULE имя ";" [СписокИмпорта] ПоследОпределений
[BEGIN ПоследПредписаний ] END имя "."

СписокИмпорта = IMPORT импорт {"," импорт } ";".
импорт = имя [":=" имя].

Приложение 2: Участники

Использован перевод:

  • Свердлова С.
  • Бурцева В.

Перевод исходного сообщения выполнили:

  • Денисов И.
  • Шипков В.

Технические консультанты исходного варианта Оберона:

  • Ершов А. П.
  • Волков С.
  • Ермаков И.
  • Ефимов А.

Дополнительные материалы:

  • чек-лист создателям компилятора

Пиложение 3: Ссылки