В связи с оживлённой дискуссией в соседней темеviewtopic.php?f=30&t=6344&start=180#p115095

хочется обсудить точки с запятой в отрыве от конкретного проекта, но в контексте Оберонов.

Краткое содержание предыдущих серий:

есть множество языков, которые прекрасно живут без точки с запятой. Однако некоторые, такие как JavaScript - не прекрасно. Помимо упомянутых в теме про Ofront, я наутро вспомнил ещё Transact-SQL. Там нет точек с запятой и конец строки тоже не имеет значения, но там любое предписание (предложение, statement) начинается с ключевого слова, в том числе и присваивание. Это облегчает задачу разбора текста, даже по сравнению с Обероном. Вообще, синтаксис легко читается, когда для его чтения не нужен глубокий стек. Именно поэтому VAR a : array 5 of pointer to array 5 of integer гораздо лучше, чем ... я что-то вообще не могу записать это на Си, редко пишу на этом языке. Как-то так, что ли?  int ((*a)[5])[5] (честно скажу, я написал это с помощью компилятора и всё равно не уверен, что правильно).
## Код:
```
#include <stdio.h>

int main()
{
    // array 5 of pointer to array 5 of integer
    int b[5];
    int (*pb)[5];
    int ((*a)[5])[5];
    
    pb = a[0];
    b[0] = (*pb)[0];
    int z = b[0];
    
    return 0;
}
```


В целом см. картинку.

Вложение:
нос-утконосонос.png [ 277.13 КБ | Просмотров: 7012 ]


В Си типы и читаются, и пишутся не с начала, не с конца, а откуда-то из середины. В голове нужно поддерживать довольно сложный парсер, чтобы читать код на Си. Это здорово, когда надо померяться, у кого длиннее извилины в мозгу, но не здорово, когда у нас ремесло и нужно получить результат - работающую программу. И потому новые языки - Go, Rust и Котлин - перешли к паскалевскому стилю определения переменных.

Так вот, с типами в Обероне всё в порядке, но присваивание это всё же десигнатор := выражение, а при том десигнатор может быть каким-то раскудрявым, например,

## Код:
```
MODULE Init;

IMPORT Log;

VAR a: ARRAY 5 OF ARRAY 5 OF INTEGER;

PROCEDURE b():INTEGER;
  BEGIN
   RETURN 1 
  END b;

PROCEDURE Do;

BEGIN
 a[3+a[2][2]][0]:=b();
 Log.Int(a[3+b()][0]); Log.Ln;
 Log.Int(b()); Log.Ln;
END Do;

BEGIN
 Do();
 Do
END Init.
```


(программа проверена наhttps://visual.sfu-kras.ru/)

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

А теперь представим себе такой кусок кода без точек с за
пятыми:
## Код:
```
Log.Int(a[3+b()][0]) a[3+a[2][2]][0]:=b()+a[3-b()][2] Log.Int(a[3+b()][0])
```

Такой код уже не так уж легко прочитать без разделителей.

В общем, я подумал-подумал и всё же склоняюсь к варианту, что:
";" обязательна";" подразумевается в конце строки";" подразумевается перед ENDпустое предписание (statement) запрещеноВсё. Если предписание слишком длинное - то уповаем на возможности среды разработки по переносу строк.

Последнее правило нужно с той целью, чтобы разделители выставлялись однозначно и не имел бы смысла код
## Код:
```
ОченьМногозначительныйВызов;;;;;;;;;;;;;;;;;;;;;;
```

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


Я долго думал над продолжением строк с помощью "\" - оно присутствует в таком уважаемом мной языке, как tcl. Однако на практике это выглядит почему-то очень уродливо. Например, потому, что если не выравнивать
пилу из "\" после каждой правки, то получается уродливо:
## Код:
```
#define max(a,b) \
   ({ __typeof__ (a) _a = (a);     \
       __typeof__ (b) _b = (b); \
     _a > _b ? _a :    \
   _b; })
```


В принципе есть ещё один вариант, не совсем хороший: не в конце строки ставить знак продолжения, а в начале следующей:
## Код:
```
#define max(a,b) 
   |({ __typeof__ (a) _a = (a);     
   |    __typeof__ (b) _b = (b); 
   |  _a > _b ? _a :    
   |_b; })
```


В вышеприведённом примере я бы ещё обратил внимание на Do/Do() - здесь как раз скобки были сделаны необязательными ради чистоты кода (другой причины я не вижу), но это приводит к тому, что в некоторых случаях компилятор путает вызов процедуры и её адрес. Это далеко не всегда безобидно, в АО скорее всего есть грабли, что
## Код:
```
PROCEDURE P:ADDRESS; BEGIN RETURN 0 END;

BEGIN
Z := P + P()
END
```


Здесь оба вызова P и P() имеют смысл, но разный (ща проверю, кстати).

P.S. С END получается нехорошо - если запретить пустой оператор, то  нельзя будет и написать BEGIN END. Значит, правила недостаточно хороши - думаем дальше. Например, оператор NOP всё равно понадобится - писать вместо "BEGIN END" - "BEGIN NOP END". В этом тоже можно найти плюсы - такая запись означает, что программист не забыл заполнить блок, а явно указал, что ничего делать не надо.