Commit c523b130 authored by Denis Budyak's avatar Denis Budyak
Browse files

Добавил стр. 31-40 (пока без вычитки)

parent 9ea6adca
......@@ -882,3 +882,352 @@ VAR a:Monochrome; b,d: ColourRGB, c: ColourCYM;
Ранее были выдвинуты два основных возражения против типа перечисление: потенциальная неоднозначность именования при импорте типа перечисление из другого модуля и отсутствие расширяемости типов. Чтобы обеспечить простое решение проблемы потенциальной неоднозначности, все идентификаторы перечисления считаются идентификаторами типа. Максимальное число идентификаторов в перечислении и значение, которое может быть им присвоено, зависит от реализации.
В языке без типов перечисление или с типами "квазиперечисление" программисты должны вручную проверить, что значения не выходят за пределы диапазона, но для больших программ это становится практически невозможным, даже для небольших программ это трудно. Например, код системы Oberon засорен группами объявлений CONST, которые обеспечивают безтиповую и подверженную ошибкам замену для типов перечисление.
#### 7.10 Активные ячейки: Тип ячейка, тип сеть ячеек и тип порт
```
ТипЯчейка = (’CELL’ | ’CELLNET’) [Флаги] [СписокПортов] [’;’] {СписокИмпорта}
ПоследовательностьОбъявлений
[Тело] ’END’ [Идентификатор].
СписокПортов = [ОбъявлениеПорта {’;’ ОбъявлениеПорта}].
ОбъявлениеПорта = Идентификатор [Флаги] {’,’ Идентификатор [Флаги]}’:’ ТипПорт.
ТипПорт = ’PORT’ (’IN’|’OUT’) [’(’ Выражение ’)’].
```
### 8 Объявления переменных
Объявления переменных устанавливают переменные, определяя для них идентификатор и тип данных. Переменные могут быть инициализированы со значением. Если значение не задано, то для указателей гарантируется инициализация с нулевым значением, а в других случаях она зависит от реализации компилятора.
```
ОбъявлениеПеременной = СписокИменПеременных [’:’ Тип].
СписокИменПеременных = ИмяПеременной {"," ИмяПеременной}.
ИмяПеременной = ОпределениеИдентификатора [Флаги]
[’:=’ Выражение | ’EXTERN’ Выражение].
Флаги = ’{’ [ Флаг {’,’ Флаг} ] ’}’.
Флаг = Идентификатор [’(’ Выражение ’)’ | ’=’ Выражение].
```
Переменные могут быть помечены как `EXTERN`, и в этом случае их идентификатор является просто псевдонимом для фиксированной области памяти. Этот адрес может быть задан выражением константы или строковым литералом, ссылающимся на объект, который определен в другом месте. Поскольку внешние переменные просто ссылаются на некоторые другие данные, они не могут быть инициализированы.
Типы в объявляемых !!! переменных могут быть опущены при наличии инициализаторов, и тип переменной должен быть определен из типа выражения.
##### 8.0.1 Инициализация переменных
Переменные с инициализатором инициируются до того, как их можно будет использовать. Обычно это означает, что инициализация выполняется в начале соответствующей секции кода:
* Если переменная является полем в объекте, она инициализируется после размещения объекта, но до вызова инициализатора объекта.
* Если переменная является полем в записи, она инициализируется при инициализации записи.
* Если переменная объявлена в области действия процедуры, она инициализируется в начале тела процедуры.
* Если переменная объявлена в области видимости модуля и инициализатор не является выражением константы, она инициализируется в начале тела модуля. Если переменная объявлена в области видимости модуля и если инициализатор является выражением константы, то переменная инициализируется в объектном файле модуля. Это может быть важно для низкоуровневого системного модуля, где код (например, процедуры с флагами `OPENING`) может быть выполнен до тела модуля.
##### 8.0.2 Примеры
```
VAR
a : REAL;
b := 10, c : INTEGER;
c* {UNTRACED} : POINTER TO ARRAY OF CHAR;
d EXTERN "BaseTypes.Pointer" : ADDRESS;
e := SomeProc(); (* type inferred *)
```
##### 8.0.3 Отличия от оригинальной версии Оберона
Флаг `EXTERN` не присутствовал в оригинальном Обероне. Кроме того, мы добавили инициализаторы переменных и определение типа переменной по типу выражения.
### 9 Объявления процедур
Объявление процедуры состоит из заголовка процедуры и тела процедуры. Заголовок задает идентификатор процедуры и формальные параметры. Для процедур, связанных с типом, в нем также указывается параметр-приемник. Тело содержит объявления и операторы. Идентификатор процедуры повторяется в конце объявления процедуры.
Существует два вида процедур: собственно процедуры и процедуры-функции. Последние активируются обозначением функции как составной частью выражения и возвращают результат, являющийся операндом выражения. Соответственно процедуры активируются вызовом процедуры. Процедура является процедурой-функцией, если в строке ее формальных параметров задан тип результата. Тело процедуры-функции должно содержать оператор `return`, который определяет ее результат.
Все константы, переменные, типы и процедуры, объявленные в теле процедуры, являются локальными для процедуры. Поскольку процедуры могут быть объявлены как локальные объекты, объявления процедур могут быть вложенными. Вызов процедуры в рамках ее объявления подразумевает рекурсивную активацию.
В добавок к ее формальным параметрам и локально объявленным объектам, в процедуре также видимы объекты, объявленные в ее окружении(за исключением тех объектов, которые имеют такое же имя, как и у объекта, объявленного локально).
```
ОбъявлениеПроцедуры = ’PROCEDURE’ [’^’|’&’|’~’|’-’|Флаги [’-’]]
[’(’ ОбъявлениеПараметра ’)’]
ОпределениеИдентификатора [ФормальныеПараметры]
[’EXTERN’ Выражение ’;’ | ’;’
ПоследовательностьОбъявлений [Тело]
’END’ Идентификатор].
ФормальныеПараметры = ’(’ [ОбъявлениеПараметра {’;’ ОбъявлениеПараметра}] ’)’
[’:’ [Флаги] Тип].
ОбъявлениеПараметра = [’VAR’|’CONST’] Идентификатор [Флаги] [’=’ Выражение]
{’,’ Идентификатор [Флаги] [’=’ Выражение]} ’:’ Тип.
Тело = ’BEGIN’ [Флаги] ПоследовательностьОператоров [’FINALLY’ ПоследовательностьОператоров]
| ’CODE’ Код.
```
Процедуры могут быть помечены как `EXTERN`, и в этом случае их идентификатор является просто псевдонимом для фиксированной области памяти. Этот адрес может быть задан выражением константы или строковым литералом, ссылающимся на объект, который определен в другом месте. Поскольку внешние процедуры просто ссылаются на некий другой код, они не имеют тела.
##### 9.0.1 Примеры
Ниже приведены некоторые примеры объявления процедуры. Последним (самым правым) формальным параметрам процедуры могут быть заданы значения по умолчанию. Впоследствии процедура может быть вызвана с меньшим количеством фактических параметров, а неуказанные параметры принимают значения по умолчанию. Если в объявлении процедуры указан параметр-приемник (как в четвертом примере ниже), процедура считается связанной с типом (здесь: тип Student).
```
PROCEDURE Send*(CONST data: ARRAY OF CHAR; ofs,len: SIZE;VAR res: INTEGER );
BEGIN (* ... *)
END Send;
PROCEDURE & Init*(scanner: Scanner.Scanner; diagnostics: Diagnostics);
BEGIN (* ... *)
END Init;
PROCEDURE Float*(x: FLOAT64; n := 4, f := 3, d := 0: INTEGER);
BEGIN
Commands.GetContext().out.FloatFix(x,n,f,d);
END Float;
PROCEDURE (CONST s: Student) GetGrade(Subject: INTEGER): REAL;
BEGIN (* ... *)
END GetGrade;
```
Если в объявлении процедуры указан параметр-приемник, процедура считается связанной с типом (см. 9.2).
#### 9.1 Формальные параметры
Формальные параметры - это идентификаторы, объявленные в списке формальных параметров процедуры. Они соответствуют фактическим параметрам, указанным в вызове процедуры. Соответствие между формальными и фактическими параметрами устанавливается при вызове процедуры. Существует два вида параметров, значение и переменная, обозначаемые в формальном списке параметров отсутствием или наличием ключевого слова `VAR`. Параметры-значение - это локальные переменные, которым в качестве начального присваивается значение соответствующего фактического параметра. Параметры-переменные соответствуют фактическим параметрам, которые являются переменными, и они обозначают эти переменные. Область действия формального параметра простирается от его объявления до конца блока процедур, в котором он объявлен. Процедура-функции без параметров должна иметь пустой список параметров. Он должен быть вызван обозначением функции, фактический список параметров которого также пуст. Тип результата процедуры не может быть ни записью, ни массивом.
Пусть `Tf` - тип формального параметра `f`, а `Ta` - тип соответствующего фактического параметра `a`. При вызовах процедуры `a` должен быть совместим с `f`. Правила совместимости определены в разделе 14.8.
#### 9.2 Процедуры, связанные с типом
Глобально объявленные процедуры могут быть связаны с типом запись, объявленным в том же модуле. В этом случае говорят, что процедуры связаны с типом запись. Связь выражается типом приемника в заголовке объявления процедуры. Приемником может быть либо переменная, либо параметр-константа записи типа `T`, либо параметр-значение типа `POINTER TO T` (где `T` - тип запись). Процедура привязана к типу `T` и считается локальной для него.
Если процедура `P` связана с типом `T0`, она также неявно связана с любым типом `T1`, который является расширением `T0`. Однако процедура `P'` (с тем же именем, что и `P`) может быть явно связана с `T1`, и в этом случае она переопределяет связывание с `P`. `P'` считается переопределением `P` для `T1`. Сигнатуры `P` и `P'` должны совпадать (см. 14.2). Если `P` и `T1` экспортируются, то `P'` тоже должна экспортироваться.
Если `v` - обозначение, а `P` - связанная процедура, то `v.P` обозначает процедуру `P`, связанную с динамическим типом `v`. Обратите внимание, что это может быть процедура отличная от той, которая связана со статическим типом `v`. `v` передается приемнику `P` в соответствии с правилами передачи параметров, указанными в разделе 9.1.
Если `r` - параметр-приемник, объявленный с типом `T`, то `r.P^` обозначает (переопределенную) процедуру `P`, связанную с базовым типом `T`. Cигнатура обоих объявлений должна совпадать (14.2).
##### 9.2.1 Примеры
```
PROCEDURE (t: Tree) Insert (node: Tree);
VAR p, father: Tree;
BEGIN p := t;
REPEAT father := p;
IF node.key = p.key THEN RETURN END;
IF node.key < p.key THEN
p := p.left
ELSE
p := p.right
END
UNTIL p = NIL;
IF node.key < father.key THEN
father.left := node
ELSE
father.right := node
END;
node.left := NIL; node.right := NIL
END Insert;
PROCEDURE (t: CenterTree) Insert (node: Tree); (* переопределение *)
BEGIN
WriteInt(node(CenterTree).width);
t.Insert^ (node) (* вызывает процедуру Insert, связанную с Tree *)
END Insert;
PROCEDURE (t: TreeNodePointer) Copy (): TreeNodePointer; (
VAR c: TreeNodePointer;
BEGIN
NEW(c); (* ... *)
RETURN c;
END Copy;
PROCEDURE (t: CenterTreeNodePointer) Copy (): TreeNodePointer; (
VAR c: CenterTreeNodePointer;
BEGIN
NEW(c); (* ... *)
RETURN c;
END Copy;
```
**Remark 4** *Обратите внимание, что тип параметра-приемника может определять динамическую природу объекта, который может принимать вызов процедуры. Если тип приемника является типом указателя, то процедура может быть вызвана только для объекта на куче. Если приемник является типом записи, то процедура может быть вызвана как для кучи, так и для стека, и невозможно, чтобы указатель кучи ускользал из этого типа.*
*Константа записей: естественно, если переменная типа запись - константа, то только приемники с параметром-константой могут принимать вызов. Это гарантирует, что свойство константы записи не может быть нарушено вызовом связанной с типом процедуры.*
#### 9.3 Оберон-2
Явный приемник для процедур с привязкой к типу позволяет отличать процедуры, изменяющие соответствующий объект, и процедуры, оставляющие его неизменным. В C++ это решается путем добавления модификатора `const` в функции-члены:
```
class Counter {
private:
unsigned int value = 0;
public:
void increment (unsigned by){
value += by;
}
unsigned current() const {
return value;
}
}
```
С помощью типа привязанных процедур один и тот же вид семантики может быть очень естественно обозначен модификаторами `var / const` параметра-приемника:
```
TYPE Counter* = RECORD value: UNSIGNED64 END;
PROCEDURE (VAR c: Counter) Increment (by: UNSIGNED64);
BEGIN
INC(c.value, by);
END Increment;
PROCEDURE (CONST c: Counter) Current (): UNSIGNED64;
BEGIN
RETURN c.value;
END Increment;
```
Более того, номенклатура очень естественно описывает то, что происходит во время вызова методов, а именно, что объект приемник передается в качестве неявного параметра процедуре.
```
VAR c: Counter;
c.Increment(10); // с передается в качестве параметра-переменной
...
ASSERT(c.Current()>=10); // c передается как параметр-константа
```
Классы в C++ обычно являются типами значений, и они могут быть выделены в куче (с помощью `new`) или стеке (неявно). Указатель на структуры / классы может указывать на кучу или стек. Указатели в этом смысле не защищены.
В Обероне (безопасные) указатели, по определению, ограничены кучей. Невозможно создать указатель на объект стека. Это подразумевает ограничение: типы запись не могут быть преобразованы в указатель. Для того чтобы создать сложные структуры данных объектов, реализуемые с использованием объектно-ориентированного подхода, в активном Обероне было введено понятие `OBJECT`. Объекты неизменно живут в куче. Синтаксически методы для объектов были определены в области видимости объекта. Очень похоже на то, как это делается на языке Java. Мы выявили большой недостаток этого подхода: каждый объект должен быть размещен в куче, даже если срок его использования очень ограничен. Это, в свою очередь, создает высокую нагрузку на сборщик мусора и затрудняет создание системы без сбора мусора вообще. Одна идея (только на синтаксическом уровне) состояла в том, чтобы заменить `OBJECT` на `RECORD` и разрешить размещение связанных с типом процедур внутри записи. Таким методам по умолчанию будет разрешено изменять содержимое объекта, то есть они будут реализованы с помощью неявного приемника `VAR`:
Код...
```
TYPE
Counter* = RECORD
value: UNSIGNED64
PROCEDURE Increment (by: UNSIGNED64);
BEGIN
INC(value, by);
END Increment;
PROCEDURE Current (): UNSIGNED64;
BEGIN
RETURN value;
END Increment;
END;
```
...будет реализован неявно следующим образом:
```
TYPE Counter* = RECORD value: UNSIGNED64 END;
PROCEDURE (VAR SELF: Counter) Increment (by: UNSIGNED64);
BEGIN
INC(value, by);
END Increment;
PROCEDURE (VAR SELF: Counter) Current (): UNSIGNED64;
BEGIN
RETURN value;
END Increment;
```
Конечно, можно было бы добавить ключевое слово `CONST` где-нибудь в определении записи точно так же, как это делается в C++, чтобы провести разницу между модифицирующим и не модифицирующим кодом. Однако есть еще одна очень важная причина остаться с элегантным и естественным объявлением связанных с типом процедур Оберона-2: можно написать связанные с типом процедуры, которые могут действовать на переменную типа запись (находящуюся в куче или в стеке), и написать связанные с типом процедуры, которые могут действовать на переменную типа указателя (находящуюся исключительно в куче). Последнее позволяет явно отличать код, который может быть вызван по указателю (и может возвращать указатель), от кода, который может быть вызван только по записи.
```
TYPE Expression = RECORD END;
TYPE PExpression = POINTER TO Expression;
(* каждое выражение может быть вычислено *)
PROCEDURE (e: Expression) Evaluate(): Value; (* ... *)
(* могут быть зарегистрированы только выражения указателей *)
PROCEDURE (e: PExpression) Register (); (*...*)
```
#### 9.4 Объявление операции
Оберон, в представленной здесь версии, поддерживает перегрузку операций. Операции могут быть объявлены следующим образом.
```
ОбъявлениеОперации = ’OPERATOR’ [Флаги] [’-’] Строка [’*’|’-’] ФормальныеПараметры ’;’
ПоследовательностьОбъявлений
[Тело]
’END’ Строка.
```
По крайней мере один из задействованных параметров должен быть определен в модуле определения операции. Перегруженные операции ищутся во всех прямо или косвенно загруженных модулях. Чтобы найти перегруженную операцию для выражения, для каждой операции вычисляется расстояние. Определяется первая операция, найденная с минимальным расстоянием. Если существуют операции с одинаковым минимальным расстоянием для выражения, то результат не определяется однозначно.
### 10 Выражения
#### 10.1 Выражения
Выражения имеют следующий вид:
```
Выражение = ВыражениеДиапазона [ОперацияОтношения ВыражениеДиапазона].
ОперацияОтношения = ’=’ | ’#’ | ’<’ | ’<=’ | ’>’ | ’>=’ | ’IN’ | ’IS’
| ’.=’ | ’.#’ | ’.<’ | ’.<=’ | ’.>’ ’.>=’
| ’??’ | ’!!’ | ’<<?’ | ’>>?’.
```
Операторы во второй и третьей строке `ОперацияОтношения` определены специально для математических массивов и подмножества активных ячеек языка, соответственно.
#### 10.2 Диапазон выражений
Выражения диапазона определяются следующим образом:
```
ВыражениеДиапазона = ПростоеВыражение
| [ПростоеВыражение] ’..’ [ПростоеВыражение][’by’ ПростоеВыражение]
| ’*’ .
```
##### 10.2.1 Отличия от оригинальной версии Оберона
Было введено понятие выражений диапазона, и таким образом диапазоны, используемые в типах множеств, были расширены до выражений, чтобы иметь возможность быть представленными в математическом Обероне.
#### 10.3 Простые выражения
```
ПростоеВыражение = Слагаемое {ОперацияСложения Слагаемое}.
ОперацияСложения = ’+’ | ’-’ | ’or’.
```
>##### 10.3.1 Отличия от оригинальной версии Оберона
>
>Унарные операторы " - " и " + " первоначально содержались здесь в простом выражении. Мы переместили их на **!!!Factor!!!**, так как считаем, что он должен иметь более высокий приоритет, подобно логической операции `not`.
>
>Мы нашли пример утверждения (в коде, который был результативным):
>
>утверждение...
>
>```
>assert(-1 MOD 3 = -1); (* не завершился ошибкой, но завершится сейчас *)
>```
>
>...было использовано для того, чтобы определить, что `a MOD 3` будет отрицательным для a < 0, что действительно неверно для Оберона. На самом деле, `(-1) MOD 3 = 2`! Это утверждение было истинным, потому что `-1 MOD 3` раньше было `-(1 MOD 3)`.
10.4 Слагаемые
```
Слагаемое = Множитель {ОперацияУмножения Множитель}.
ОперацияУмножения = ’*’ | ’/’ | ’DIV’ | ’MOD’ | ’&’
| ’.*’ | ’./’ | ’\’ | ’**’ | ’+*’ .
```
Операции, указанные во второй строке `ОперацияУмножения`, определены специально для математических массивов.
10.5 Множители
Множители определяются следующим образом
```
Множитель = УнарноеВыражение | УнарнаяОперация Множитель.
УнарнаяОперация = ’~’ | ’+’ | ’-’.
```
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment