Оберон-клуб «ВЄДАsoft»

Твердыня модульных языков
Текущее время: 29 мар 2024, 00:09

Часовой пояс: UTC + 2 часа




Начать новую тему Ответить на тему  [ Сообщений: 22 ]  На страницу 1, 2, 3  След.
Автор Сообщение
СообщениеДобавлено: 20 авг 2016, 13:18 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
При внесении изменений в константные массивы из элементов CHAR, вдруг обнаружилась странная особенность Оберона, на которую до сих пор не обращали внимания - приоритет операции "унарный минус".

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

Получается, что (-1 MOD 256) означает "-(1 MOD 256)" , а не "(-1) MOD 256". Кроме того, "-1" это не литерал "минус единица", а константное выражение "изменение знака у литерала 1", в результате вычисления которого получается константа "минус 1". Если бы унарный минус имел наивысший приоритет, то не было бы практической разницы для записи "-1" (и литерал и константное выражение означало бы "минус единица"). Но если приоритет унарного минуса ниже мультипликативных операций, то разница есть: (-1 MOD 256) = -1, а выражение "(-1) MOD 256" дает 255.

Выражение "-1 MOD 256" я бы прочитал как "минус единица, взятая по модулю 256", а в Обероне это "изменение знака у единицы, взятой по модулю 256". Графически знак "-" занимает меньшую площадь, чем слово MOD, поэтому зрительно "связывает сильнее", т.е. из соображений наглядности "сначала минус, а потом MOD". Числа бывают положительные и отрицательные, "-" воспринимается как часть числа, а не как операция. Но почему-то сделано наоборот. И это коварная ловушка, в которую вляпаются новички.

Много коварство таит Си, но там унарный минус сделан лучше. В Паскале, Си, Бейсике (по крайней мере, в большинстве диалектов) и многих других языках унарный минус имеет наивысший приоритет (во всяком случае, выше мультипликативных операций).

Почему же в Обероне не так? На форуме oberoncore.ru ясных ответов на этот вопрос пока не дали. В личной переписке с Zorko мы решили было, что приоритет унарного минуса понизили до такого как у аддитивных операций исключительно чтобы упростить: a) описание языка; b) компилятор.

Но оказалось, сменить приоритет очень легко. Сейчас формы Бэкуса-Наура для выражения такие:
Код: "OBERON"
  1. Expression = SimpleExpression [Relation SimpleExpression]
  2. SimpleExpression = ["+" | "−"] Term {AddOperator Term}
  3. Term = Factor {MulOperator Factor}
  4. Factor = Designator [ActualParameters] | number | character |
  5. string | NIL | Set | "(" Expression ")" | "˜" Factor
  6. Set = "{" [Element {"," Element}] "}"
  7. Element = Expression [".." Expression]
  8. ActualParameters = "(" [ExpressionList] ")"
  9. Relation = "=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS
  10. AddOperator = "+" | "−" | OR
  11. MulOperator = "*" | "/" | DIV | MOD | "&"
Для изменения приоритета унарного минуса (и плюса, заодно), достаточно изменить 2 правила:
Код: "OBERON"
  1. SimpleExpression = Term {AddOperator Term}
  2. Factor = Designator [ActualParameters] | number | character |
  3. string | NIL | Set | "(" Expression ")" |("˜" |"+" | "−") Factor
Из SimpleExpression убрать, в Factor поставить - сложнее не стало.
Изменить разбор выражений по этой новой грамматике в модуле OPP тоже не сложно:
Код: "OBERON"
  1. PROCEDURE Factor(VAR x: OPT.Node);
  2. VAR fpar, id: OPT.Object; apar: OPT.Node;
  3. BEGIN
  4. IF (sym < lparen) & (sym # minus) & (sym # plus) THEN err(13); (* чем может начинаться Factor*)
  5. REPEAT OPS.Get(sym) UNTIL sym >= lparen
  6. END;
  7. ...
  8. ELSIF sym = not THEN
  9. OPS.Get(sym); Factor(x); OPB.MOp(not, x)
  10. ELSIF sym = minus THEN (* унарный минус *)
  11. OPS.Get(sym); Factor(x); OPB.MOp(minus, x)
  12. ELSIF sym = plus THEN (* унарный плюс *)
  13. OPS.Get(sym); Factor(x); OPB.MOp(plus, x)
  14. ELSE err(13); OPS.Get(sym); x := NIL
  15. END ;
  16.  
  17. ...
  18. PROCEDURE SimpleExpression(VAR x: OPT.Node);
  19. VAR y: OPT.Node; addop: BYTE;
  20. BEGIN
  21. (* убираем + и - из SimpleExpression *)
  22. (* IF sym = minus THEN OPS.Get(sym); Term(x); OPB.MOp(minus, x)
  23. ELSIF sym = plus THEN OPS.Get(sym); Term(x); OPB.MOp(plus, x)
  24. ELSE Term(x)
  25. END ; *)
  26. Term(x);
  27. WHILE (plus <= sym) & (sym <= or) DO
  28. addop := sym; OPS.Get(sym);
  29. Term(y); OPB.Op(addop, x, y)
  30. END
  31. END SimpleExpression;
  32. IF x = NIL THEN x := OPB.NewIntConst(1); x^.typ := OPT.undftyp END
  33. END Factor;
Как видим, никакого усложнения ни в грамматике, ни в трансляторе.

Но вот стоит ли менять стандарты Оберона? Одно дело исправить FOR, потому что вряд ли кто-то использовал полезный эффект от зацикливания на краю диапазона. Другое дело - ломать устоявшийся и прошедший через все версии стандарт. По крайней мере до того, как будет понятно, почему именно такой стандарт принят.


Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 20 авг 2016, 20:05 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
Мы заодно повысили приоритет и унарного плюса, но это не имеет значения. В Обероне унарный плюс выполняется как тождественная операция (т.е .пустая), поэтому не важно в какой именно момент она будет выполнена. Унарный плюс пропадает уже при построении дерева, в С-листинг эта операция не попадает. В OPB.MOp это делает строка:
Код: "OBERON"
  1. | plus:
  2. IF ~(f IN intSet + realSet) THEN err(96) END
Т.е. единственное, что совершается - контроль, что унарный плюс применен к числовому типу. И это правильно, потому что в некоторых С-компиляторах унарный плюс отнюдь не пустая операция. Например, в TC унарный плюс запрещает компилятору реорганизовывать скобочные выражения. Поэтому с плюсом и без него могут получиться разные результаты (хотя разница проявляется только при переполнении или побочных эффектах).

Приоритет унарного минуса для чисел проявляется, пожалуй, только в сочетании с MOD. Прочие мультипликативные (*, DIV или / ) позволяют вносить знак внутрь, и там приоритет может проявиться только в переполнении на краю диапазона (из-за несимметричности диапазона целых значений относительно нуля).

Но унарный оператор "-" применяется также для множества, тогда он означает дополнение. А в этом случае разница более заметна, потому что для множеств (-A)*B # -(A*B). Аналогично для объединения "+" и разности "-". Но и в этом случае логичнее, если дополнение имеет приоритет выше, чем прочие операции над множествами.

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


Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 23 авг 2016, 08:13 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
И если уж унарный минус получил высший приоритет, можно пойти дальше. Можно считать унарный минус перед числовым литералом частью этого литерала, а не унарной операцией. Тогда не возникнет, например, вопросов о "магическом" числе "-2147483648". Почему-то такое число есть в диапазоне, но нельзя написать прямо n:=-2147483648, а следует писать n:=-2147483647-1.
Конечно, этому есть простое объяснение. Запись "-2147483648" не числовой литерал, а константное выражение "изменение знака у литерала (положительной константы) 2147483648". Однако, положительной константы 2147483648 не существует, поэтому и возникает ошибка.
Если толковать унарный "-" перед цифрами как знак числа, а не операцию, то таких проблем не возникнет. Но на уровне сканера (в модуле OPS) сделать это затруднительно, потому что сканеру трудно отличить унарный минус от операции вычитания.
Но можно сделать следующим образом. Если сканер прочитает "-", после которого идет цифра, то вместо этого минуса, сканер выдает "+". Константу, стоящую далее, сканер формирует как отрицательную. Т.е. при операциях с числовыми литералами унарный минус толкуется как две унарные операции "+-", а вычитание толкуется как сложение с отрицательной константой. Недостаток такого подхода - лишний плюс в С-листинге (сложение с отрицательной константой вместо вычитания). Необходимо обдумать, грозит ли это ещё какими-то проблемами кроме эстетических?


Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 25 авг 2016, 08:00 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
Saferoll писал(а):
Недостаток такого подхода - лишний плюс в С-листинге (сложение с отрицательной константой вместо вычитания). Необходимо обдумать, грозит ли это ещё какими-то проблемами кроме эстетических?
Унарный плюс никогда не попадает в С-листинг, лишний + всегда означает сложение (бинарный оператор). Можно убирать лишний плюс при построении дерева или при генерации С-кода. При этом нельзя заменять "n:=a+-2147483648" на "n:=a-2147483648", потому что нет константы 2147483648.


Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 25 авг 2016, 11:16 
Не в сети
Аватара пользователя

Сообщения: 1019
Откуда: Днепропетровская обл.
Я тут вычитал в репорте на Оберон-07 такую строчку:
Цитата:
When used with a single operand of type SET, the minus sign denotes the set complement.
Может быть, что такое представление унарного минуса вызвано, отчасти, желанием унифицировать эту операцию, наряду с числовой, ещё и для множеств?

С интересом читаю твои рассуждения. В плане реализации вроде как предложенное тобою +(-n) не должно вызвать проблем, но выглядит как хак, или не? :-)


Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 25 авг 2016, 15:39 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
Zorko писал(а):
Я тут вычитал в репорте на Оберон-07 такую строчку:
Цитата:
When used with a single operand of type SET, the minus sign denotes the set complement.
Может быть, что такое представление унарного минуса вызвано, отчасти, желанием унифицировать эту операцию, наряду с числовой, ещё и для множеств?
Может быть, но даже когда унарный минус означает дополнение множества, логичнее, если дополнение имеет приоритет выше, чем прочие операции над множествами. Т.е. и в этом случае нужен наивысший приоритет.
Цитата:
В плане реализации вроде как предложенное тобою +(-n) не должно вызвать проблем, но выглядит как хак, или не?
Хак и есть. :)
Чем отличается хак от "нормальной" модификации? Хак добивается цели неестественным образом, ставится в том месте, где это было не предусмотрено и нарушает нормальную логику работы. Пусть даже сейчас хак и работает, но он может нарушить работу "нормальной" модификации, которая сделана после него. Поэтому, после хака все следующие модификации тоже должны быть хаками. И постепенно вся программа превратится в сплошной хак, когда проще всё выкинуть и написать заново, чем модифицировать. :shock:

Между хаком и нехаком тонкая граница. Но в данном случае плохо то, что изменения происходят на уровне сканера. Сканер не может отличить унарный минус от вычитания, поэтому предлагается считать частью константы ЛЮБОЙ минус, после которого стоит цифра. Но тогда в вычитании операция пропадет вообще, что вызовет ошибку, поэтому добавляем +. Для унарного минуса этот плюс будет означать унарный +, т.е. не помешает.

Если это на уровне сканера, то для вышестоящих уровней выглядит так, как будто этот лишний + был во входном тексте. Цифра после минуса может встретится в комментариях или строковых константах или как знак показателя (после Е), но это обрабатывается отдельно. Минус используется также для множеств (но константа-множество не начинается с цифры), после слова PROCEDURE (но имя процедуры не начинается цифрой), как пометка экспорта (но дальше не может быть цифры).

Для синтаксически верного текста проблем быть не должно, но возможны нарушения в индикации ошибок для ошибочного текста. Потому что в ошибочном тексте цифра может быть вычтена из множества или начинать имя процедуры. В этом случае вылезет лишний плюс (например "PROCEDURE-4" превратится в "PROCEDURE+-4"), на который будет выдана ошибка. Но ошибка должна быть выдана на 4, а не на + (которого в исходном тексте вообще нет).

К тому же, такой хак (а это именно он!), помешает дальнейшему расширению. Например, если мы задумаем какой-нибудь системный параметр ARRAY[-1], нам придется вспомнить, что сканер это превратит в ARRAY[+-1] и обрабатывать +, хотя здесь в [] стоит не арифметическое выражение.

Так что это плохой путь, лучше "подняться повыше" - например на уровень парсера. Хотя на уровне сканера всё-равно придется как-то зафиксировать константу "-2147483648", ведь обычным путем (как минус и отдельное положительное число) это невозможно.


Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 26 авг 2016, 09:47 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
Итак - следующий хак. :)
Одна из идей - расширить диапазон положительных целых литералов, считываемых сканером. Пусть сканер возвращает через intval значение константы с противоположным знаком. Т.е. положительное значение будет возвращаться как отрицательное. А на уровне парсера при чтении константы просто будем изменять знак на противоположный, если это возможно. Поскольку отрицательных чисел больше, то хватит места всем положительным. А литерал 2147483648 будет возвращен, как intval=-2147483648, а затем либо превратится в константу "-2147483648", если перед ним унарный минус, либо в ошибку (в ином случае).

Но как бы не так! Оказывается, сканер может возвращать и отрицательные целые константы. Это происходит, если константа задана в hex-виде "0xxxxxxxxH". Если литерал записан максимальным количеством 16-ричных цифр (равно константе OPM.MaxHDig) и старшая цифра больше 7, то считается, что задана отрицательная целая константа в дополнительном коде. Т.е. hex-представление содержит внутри себя знак числа (в виде старшего бита).

Получается, что в десятичном виде можно задать только положительный литерал, а отрицательные числа нужно задавать константным выражением с использованием унарного минуса. А число "-2147483648" следует задавать как константное выражение (-2147483647-1), но не (-1073741824*2), потому что умножение выполняется раньше изменения знака.
В то же время, в 16-ричном виде константа передается в дополнительном коде, что позволяет передать знак числа старшим битом. Однако, перед такой константой тоже может стоять "-". Можно ли его трактовать в этом случае как ещё один знак числа? Или считать минус перед цифрой знаком числа, только если последующие цифры задают десятичную константу или 16-ричную неотрицательную константу? А если написано "--80000000H", то как трактовать это?

Кстати, если после минуса стоит пробел, то это явно унарная операция, а не знак числа. Поэтому "-2147483648" должно пониматься (после нашей доводки) как отрицательный литерал, а "- 2147483648" как изменение знака у положительной константы 2147483648 (что вызовет ошибку). И разница тут только в малозаметном пробеле.

Не из-за этих ли сложностей и понижен приоритет унарного минуса? Чтобы уж точно путаницы между операцией и частью литерала не было...


Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 26 авг 2016, 17:22 
Не в сети
Аватара пользователя

Сообщения: 1019
Откуда: Днепропетровская обл.
Ну да, значащий пробел... потом маленько украшаем форматирование, и понеслось. Привет, Фортран! ;-)

А как интерпретировать такое?
Код: "OBERON"
Может всё-таки не считать пробел значащим? Унарность строго определяется не пробелом, а наличием операнда слева.

А такое разрешать?
Код: "OBERON"
( всего-навсего ряд изменений знака после унарного плюса )

Я всецело за повышение приоритета ун. минуса, однако смущают все эти тонкости. :oops:


Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 27 авг 2016, 09:59 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
Zorko писал(а):
Ну да, значащий пробел... потом маленько украшаем форматирование, и понеслось. Привет, Фортран! ;-)
...
Может всё-таки не считать пробел значащим? Унарность строго определяется не пробелом, а наличием операнда слева.
Пробел здесь является значимым в том же смысле, как внутри числа или внутри ключевого слова. Если написать "1 23" вместо "123", или "WH ILE" вместо "WHILE" то будет выдана ошибка. Если пробел или комментарий или разрыв строки поставить между токенами, то никакого эффекта не будет. Один результат даст: "a :=-100;" или "a :=- 100;" или "a :=- (* комментарий *) 100;" или
Код: "OBERON"
Хотя (после доработки) в первом случае "-" это часть литерала, а в остальных это унарный оператор. Разница будет для минимального числа:

"-2147483648" это отрицательный литерал, здесь минус часть литерала (знак числа), ошибки не вызовет.
"- 2147483648" это унарный минус, после которого стоит положительный литерал, выходящий за диапазон. Здесь будет ошибка.

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

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

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


Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 27 авг 2016, 11:54 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
Вот обсуждение сходной темы: MIN(LONGINT) written as number doesn't work on 64 bit

И сразу возникает вопрос о границах диапазона целого типа. Чем эти границы должны определяться?
Почему-то сейчас они определяются через типы КП ,т.е. определяются платформой BlackBox, на которой запущен транслятор:
Код: "OBERON"
  1.  
  2. MaxHDig* = 16;
  3. IF (n = OPM.MaxHDig) & (dig[0] > "7") THEN (* prevent overflow *) intval := -1 END;
  4. ...
  5. IF intval <= (MAX(LONGINT) - d) DIV 10 THEN intval := intval*10 + d
Но, ИМХО, константы которые встречаются в транслируемой программе должны задаваться не средой в которой работает транслятор, а типами транслируемой программы. Т.е. максимальное количество 16-ричных цифр и наибольшая (наименьшая) константы должны задаваться параметрами Ofront.par. Это означает, что в модуле OPM должен быть определен параметр OPM.MaxHDig := 2*LIntSize; (каждый байт содержит две hex-цифры), а при чтении целой константы нужно контролировать
Код: "OBERON"
  1. IF intval <= (OPM.MaxLInt - d) DIV 10 THEN intval := intval*10 + d
Аналогично следует поступить и для вещественных констант - их границы должны задаваться параметрами MinLReal, MaxLReal.

В процедуре OPM.WriteInt тоже почему-то граница задаётся типами КП:
Код: "OBERON"
  1. IF (i = MIN(INTEGER)) OR (i = MIN(LONGINT)) THEN (* requires special bootstrap for 64 bit *)
  2. (* ABS(MIN(INTEGER)) is one more than MAX(INTEGER), causing problems representing the value
  3. as a minus sign followed by absoute value. Therefore represent as -MaxInt - 1.
  4. For INTEGER this avoids a compiler warning 'this decimal constant is unsigned only in ISO C90'.
  5. For LONGINT it is the only way to represent MinLInt. *)
  6. Write("("); WriteInt(i+1); WriteString("-1)")
В данном случае эта граница должна задаваться типами С-компилятора, а они всё-таки ближе к типам транслируемой программы. Поэтому здесь нужно
Код: "OBERON"
  1. IF (i = MinInt) OR (i = MinLInt) THEN


Вернуться к началу
 Профиль  
Ответить с цитатой  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 22 ]  На страницу 1, 2, 3  След.

Часовой пояс: UTC + 2 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 5


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
Создано на основе phpBB® Forum Software © phpBB Group
© VEDAsoft Oberon Club