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

Твердыня модульных языков
Текущее время: 30 дек 2024, 18:52

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




Начать новую тему Ответить на тему  [ Сообщений: 69 ]  На страницу 1, 2, 3, 4, 5 ... 7  След.
Автор Сообщение
 Заголовок сообщения: Константные массивы
СообщениеДобавлено: 27 июн 2014, 23:35 
Не в сети
Аватара пользователя

Сообщения: 1019
Откуда: Днепропетровская обл.
Ещё наверное будет нелишним ответить на вопрос: зачем вообще приходится городить что-то подобное вышенаписанному? Да дело в том, что в Обероне (1, 2, 07 и КП) нет константных массивов. Ресурсы там предлагается подгружать из файлов или присваивать поэлементно.

Считаю этот недостаток Оберона, пожалуй, самым досадным. Я очень хочу его исправить хотя бы в XDev, тем более, подобные попытки уже делались в Active Oberon, Extacy Oberon-2, Amiga-Oberon v3.11, F. Siebert / A+L AG. И синтаксис предложен:
Код: "OBERON"
  1. TYPE TheClocks = ARRAY 10 OF INTEGER;
  2. CONST AnimClockData = TheClocks(
  3. 00400, 00070, 00000, 00700, 00100, 00380, 00000, 00700, 03008, 00000);
Эмпирически можно догадаться, что этот массив изменять нельзя. Меня устраивает такое расширение Оберона. И синтаксис подходящий. Дело только за реализацией, но, предчувствую, будет сложновато: это направление нужно хорошенько изучить перед тем как садиться кодить.


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 29 июн 2014, 14:39 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
Код: "OBERON"
  1. TYPE TheClocks = ARRAY 10 OF INTEGER;
  2. CONST AnimClockData = TheClocks(
  3. 00400, 00070, 00000, 00700, 00100, 00380, 00000, 00700, 03008, 00000);
А что конкретно должно получиться в Си-программе из этого описания?
Код: "OBERON"
  1. const int AnimClockData [10] = {00400, 00070, 00000, 00700, 00100, 00380, 00000, 00700, 03008, 00000};

или что-то иное?


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 29 июн 2014, 20:19 
Не в сети
Аватара пользователя

Сообщения: 1019
Откуда: Днепропетровская обл.
Так точно. А для компиляторов чистого Си, в которых не предусмотрено слово const (а такие компиляторы есть, ибо const появилось в C++) можно вынести определение в SYSTEM.h:
Код: "C"
#ifdef __cplusplus
# define __CONSTARR const
#else
# define __CONSTARR
#endif
Так будет работать на большем количестве компиляторов.

Меня в реализации этой фичи смущает то, что мы этим подошли к заданию явно типизированных констант, а значит логично будет поддержать не только массивы, а и что-то такое:
Код: "OBERON"
  1. CONST a = LONGINT(0);
Или это уже лишнее, как думаешь?


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 30 июн 2014, 21:00 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
Zorko писал(а):
Меня в реализации этой фичи смущает то, что мы этим подошли к заданию явно типизированных констант, а значит логично будет поддержать не только массивы, а и что-то такое:
Код: "OBERON"
  1. CONST a = LONGINT(0);
Или это уже лишнее, как думаешь?
А типизированные константы будут чем-то отличаться от обычных?
0 - это константа типа SHORTINT, т.к. это наменьший диапазон, ее содержащий.
Код: "OBERON"
  1. CONST a = LONG(LONG(0));
- это то же самое, что и LONGINT(0) или нет? Для типизированных констант будут какие-то особые механизмы хранения или еще что-то?


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 02 июл 2014, 00:30 
Не в сети
Аватара пользователя

Сообщения: 1019
Откуда: Днепропетровская обл.
В языке Оберон (1, 2, 07 и КП) тип константы берётся из контекста описания, и вобщем-то это достаточно удовлетворительно. Не знаю как компилятор хранит константы внутренне, но совместимость SHORTINT => INTEGER => LONGINT => REAL позволяет указать внутренний тип константы в минимальный, а потом при подстановке значения приводить по мере необходимости к большему типу если это потребуется. Но можно назвать пару случаев (их наверное может быть больше) когда имеет место неоднозначность:
  • Код: "OBERON"
    1. CONST x = 0; BEGIN SYSTEM.PUT(addr, x);
    2. (* PUT(a, x) x: any basic type, pointer type, procedure type *)
    3. (* Будет записан байт, слово, двойное слово или четверное слово? *)
    4. (* Для предотвращения неоднозначности в AO есть PUT8, PUT16, PUT32 и PUT64 *)
    И я уверен, что CONST a = LONG(LONG(0)) здесь не поможет.

  • Цитирую статью Патрика Реали «Критика языка Oberon»:
    Цитата:
    1.2 Неоднозначный тип константы

    Символьные константы и односимвольные строки имеют одну и ту же нотацию ("x"). Это является проблемой для компилятора, т.к. тип константы зависит от контекста, в котором она используется. Когда она передаётся как фактический параметр в ARRAY OF SYSTEM.BYTE или используется приведение типа при помощи SYSTEM.VAL, тип константы не может быть определён.

    Есть три возможных решения данной проблемы: 1) использовать различную нотацию для символьных констант и строк, 2) сделать константу CHAR совместимой со строками, и 3) сделать односимвольные строки совместимыми с константами CHAR. Первое решение самое лучшее, но выглядит непрактичным, т.к. придется переписывать большое количество программ; мы считаем, что "x" должно трактоваться как строка, и что строки длины 1 должны быть совместимы с CHAR.

Так что не знаю насколько большой будет доработка Ofront'а. Но, в принципе, если уж разрешать такое прямое уточнения типа константы:
Код: "OBERON"
  1. TYPE TheClocks = ARRAY 10 OF INTEGER;
  2. CONST AnimClockData = TheClocks(
  3. 00400, 00070, 00000, 00700, 00100, 00380, 00000, 00700, 03008, 00000);
то будет логично распространить его и на остальные типы независимо от того, много ли случаев неоднозначной трактовки типа констант может случиться. Кстати, насколько я помню, в Active Oberon (и как раз с подачи Патрика Реали) есть возможность задать тип константы. Также (если правильно помню) в AO есть и константные массивы.

Ещё, кстати, как быть, разрешать ли такое?
Код: "OBERON"
  1. CONST AnimClockData = ARRAY 10 OF INTEGER(
  2. 00400, 00070, 00000, 00700, 00100, 00380, 00000, 00700, 03008, 00000);


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 02 июл 2014, 00:46 
Не в сети
Аватара пользователя

Сообщения: 1019
Откуда: Днепропетровская обл.
Проверил. TurboPascal 7.1 такое компилирует:
Код: "OBERON"
  1. PROGRAM ConstArr;
  2.  
  3. TYPE
  4. TheClocks = ARRAY [0..1] OF INTEGER;
  5. CONST
  6. AnimClockData: TheClocks = (400, 70);
  7. AnimClockData2: ARRAY [0..1] OF BYTE = (4, 7);
  8. z: BYTE = 0; z1: INTEGER = 0; z2: REAL = 0;
  9.  
  10. BEGIN
  11. END.
Впрочем, насколько мы помним, такая константа в TP не совсем константа, поэтому компилируется и это:
Код: "OBERON"
  1. BEGIN
  2. z := 1; z1 := 2; z2 := 1.1;
  3. AnimClockData[0] := AnimClockData2[0];
  4. END.
Но в нашей модификации Ofront'а конечно хотелось бы запретить такие присваивания, оставляя константы константами, а не проинициализированными переменными, попавшими в секцию CONST по недоразумению.


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 02 июл 2014, 20:32 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
Zorko писал(а):
И я уверен, что CONST a = LONG(LONG(0)) здесь не поможет.
Действительно, тип константы определяется ее значением. Так числовая константа в какой наименьший тип влезла, такого типа она и есть. Вместо константы-литерала везде можно использовать константное выражение, но после вычисления такого выражения получается число. И тип выражения определяется этим числом, как если бы оно было изначально задано. Так что, LONG(0) и 0+0 и 0-0*0 дают в результате "0", а значит представляют собой просто другой способ записи "константы ноль".
Вот переменные - другое дело, тут тип определяется не текущим значением, а потенциальными возможностями хранилища. Пусть сейчас там ноль, но ведь может быть и другое значение. Если VAR x:INTEGER, то выражение LONG(x) имеет тип LONGINT (даже если х=0 в данный момент).
В ТурбоПаскале были так называемые "типизированные константы", которые по сути были не константами, а переменными. Их можно бы назвать "преинициализированными переменными с глобальным временем жизни".
А вот константа CONST a = LONGINT(0); больше заслуживает название "типизированная" - это константа у которой есть не только значение, но и особый тип (к значению дополнительно приклеен "ярлык", "тэг" с названием типа). Переменная обязана уметь хранить любое значение своего типа, для чего ей выделена память соответствующего размера. У типизированной константы значение уже задано и не изменится, так что память для всего диапазона значений можно не выделять, но компилятор где-то должен хранить ее тип.
Типизированная константа (настоящая, а не турбопаскалевская) - это некий гибрид между переменной и константой, в большей степени это константа, но на переменную она похожа наличием типа. Введение таких констант в компилятор усложнит вычисление константных выражений. Сейчас выражение из констант сначала вычисляется, а потом по результату определяется его тип. При наличии типизированных констант тип придется вычислять непрерывно, одновременно со значением.
Но это и правда снимет неоднозначности в некоторых случаях, например таким образом можно будет задавать знаковые и беззнаковые константы (когда таковые появятся).
Цитата:
Так что не знаю насколько большой будет доработка Ofront'а. Но, в принципе, если уж разрешать такое прямое уточнения типа константы:
Код: "OBERON"
  1. TYPE TheClocks = ARRAY 10 OF INTEGER;
  2. CONST AnimClockData = TheClocks(
  3. 00400, 00070, 00000, 00700, 00100, 00380, 00000, 00700, 03008, 00000);
то будет логично распространить его и на остальные типы независимо от того, много ли случаев неоднозначной трактовки типа констант может случиться. Кстати, насколько я помню, в Active Oberon (и как раз с подачи Патрика Реали) есть возможность задать тип константы. Также (если правильно помню) в AO есть и константные массивы.
А вот константные массивы - это уже нечто другое, чем LONGINT(0). Потому что, числовые константы были изначально, пусть даже их тип задавался значением, а не явным указанием. А вот массивов-констант какого либо типа не было (за исключением, разве что, цепочек символов). Как они должны толковаться семантически? Как IN-параметры, полученные откуда-то извне? AnimClockData[0] - это константное выражение? Можем ли мы передать такой массив в процедуру фактическим параметром, только ли по значению или как IN тоже можно?
Обычная константа задается только своим значением и это значение собственно сама константа и есть. Типизированная константа кроме значения имеет тип, поэтому INTEGER(0) и LONGINT(0) имеют одинаковое значение и разный тип. Константный массив видимо имеет еще и адрес, который и передается в процедуры.
Цитата:
Ещё, кстати, как быть, разрешать ли такое?
Код: "OBERON"
  1. CONST AnimClockData = ARRAY 10 OF INTEGER(
  2. 00400, 00070, 00000, 00700, 00100, 00380, 00000, 00700, 03008, 00000);

А это уже константный массив анонимного типа. Навроде,
Код: "OBERON"
  1. TYPE TheClocks = ARRAY 10 OF INTEGER;
  2. CONST AnimClockData = TheClocks(
  3. 00400, 00070, 00000, 00700, 00100, 00380, 00000, 00700, 03008, 00000);
, но идентификатор типа TheClocks спрятан, так что мы не можем его нигде употребить. Значит не можем задать переменную или параметр такого типа, поэтому не можем получить "Одинаковые типы [Same types]" и значит "эквивалентные типы" тоже не получим и вообще мало что сможем с таким массивом сделать. Если, конечно, не расширим понятия совместимости, как это расширено для строк (именно потому, что строки задаются "константными массивами анонимных типов").

Лучше бы вводить новые фичи постепенно, исходя из практической потребности. Скажем, для начала можно ввести константные массивы неанонимного типа, семантически толкуя их как полученные извне IN-параметры. Это, думаю, будет проще реализовать и пока для практики достаточно.


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 02 июл 2014, 21:36 
Не в сети
Аватара пользователя

Сообщения: 1019
Откуда: Днепропетровская обл.
Да, наверное ты прав, Олег. Так и нужно сделать.


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 03 июл 2014, 17:29 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
Ну и пока, я думаю, следует ограничиться одномерными массивами (константные выражения в скобках через запятую).
Тогда план реализации такой:
1) Находим в парсере (модуль ОРР) то место, что генерирует ошибку на TheClocks в строчке CONST AnimClockData = TheClocks (...)
2)Ставим там условие, что прочитанный идентификатор является одномерным массивом из ... ну на первых порах можно числовыми типами ограничиться.
3)Если это действительно так, то создаем в памяти кусок для хранения этого массива (тип элементов и их количество ведь известны)
4)Дальше берем кусок кода из ActualParameters, потому что элементы в скобках синтаксически похожи на список фактических параметров. И если ActualParameters строит список параметров fpar, то здесь нужно помещать считанные константные выражения в наш выделенный кусок. Конечно, нужно по пути контролировать тип и константность. И количество тоже проконтролировать.
5) Получили новый узел в дереве со ссылкой на эти элементы. Этот узел нового типа, поэтому как-то нужно это пометить.
6) В модуле OPV в том месте, который обрабатывает узлы такого типа, пишем генерацию текста на Си: выводим элементы через запятую по столько-то в строке (по 10 или 16, например).

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


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 13 июл 2014, 19:58 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
Приступил к реализации этого плана:
Saferoll писал(а):
1) Находим в парсере (модуль ОРР) то место, что генерирует ошибку на TheClocks в строчке CONST AnimClockData = TheClocks (...)
2)Ставим там условие, что прочитанный идентификатор является одномерным массивом из ... ну на первых порах можно числовыми типами ограничиться.
1,2) место в модуле ОРР нашел -это PROCEDURE Block, самое ее начало:
Код: "OBERON"
  1. PROCEDURE Block(VAR procdec, statseq: OPT.Node);
  2. VAR typ: OPT.Struct;
  3. obj, first, last: OPT.Object;
  4. x, lastdec: OPT.Node;
  5. i: SHORTINT;
  6. BEGIN first := NIL; last := NIL; nofFwdPtr := 0;
  7. LOOP
  8. IF sym = const THEN
  9. OPS.Get(sym);
  10. WHILE sym = ident DO
  11. OPT.Insert(OPS.name, obj); CheckMark(obj^.vis);
  12. obj^.typ := OPT.sinttyp; obj^.mode := Var; (* Var to avoid recursive definition *)
  13. IF sym = eql THEN
  14. OPS.Get(sym);
  15. IF sym = ident THEN (* если это идентификатор *)
  16. x := OPT.NewNode(Ntype); qualident(x^.obj); (* берем его как идентификатор типа *)
  17. IF (x^.obj^.mode = Typ) & (x^.obj^.typ^.form = Comp) & (x^.obj^.typ^.comp = Array)
  18. THEN ConstArray(x)
  19. ELSE ConstExpression(x) (* обрабатываем стандартным образом *)
  20. END
  21.  
  22. ELSE
  23. ConstExpression(x)
  24. END
  25. ELSIF sym = becomes THEN ...

Цитата:
3)Если это действительно так, то создаем в памяти кусок для хранения этого массива (тип элементов и их количество ведь известны)
4)Дальше берем кусок кода из ActualParameters, потому что элементы в скобках синтаксически похожи на список фактических параметров. И если ActualParameters строит список параметров fpar, то здесь нужно помещать считанные константные выражения в наш выделенный кусок. Конечно, нужно по пути контролировать тип и константность. И количество тоже проконтролировать.
3,4) Пишем новую процедуру ConstArray(x) на основе ActualParameters. Можно поместить ее внутри процедуры Block, но я вынес наружу перед Block (возможно константные массивы понадобятся в дальнейшем где-то еще):
Код: "OBERON"
  1. PROCEDURE ConstArray (VAR x: OPT.Node);
  2. VAR apar: OPT.Node; n: INTEGER; fp: OPT.Object;
  3. BEGIN
  4. IF x^.obj^.typ^.BaseTyp^.form IN intSet THEN (* массив из целых, позднее добавим тип BYTE *)
  5. n:=x^.obj^.typ^.n; (* количество элементов массива *)
  6. CheckSym(lparen); (* скобка ( *)
  7. fp := OPT.NewObj(); fp^.typ:= x^.obj^.typ^.BaseTyp;
  8. fp^.mode := Var; (* fp - переменная, элемент массива *)
  9. (* тут нужно бы выделить кусок памяти для n элементов массива *)
  10. IF sym # rparen THEN
  11. LOOP ConstExpression(apar); (* берем очередной элемент *)
  12. IF n # 0 THEN
  13. OPB.Param(apar,fp); (* проверим на совместимость*)
  14. (* тут нужно положить элемент в кусок памяти *)
  15. DEC(n);
  16. ELSE err(64)
  17. END ;
  18. IF sym = comma THEN OPS.Get(sym)
  19. ELSIF (lparen <= sym) & (sym <= ident) THEN err(comma)
  20. ELSE EXIT
  21. END
  22. END
  23. END ;
  24. IF n # 0 THEN err(65) END;
  25. CheckSym(rparen);
  26. x:=apar; (* пока так для тестирования, а вообще-то тут нужно создать узел "константный массив" *)
  27. ELSE err(51)
  28. END
  29. END ConstArray;
Пока это только заготовка, в ней константный массив превращается в одну константу - его последний элемент. Но уже можно протестировать реакцию на ошибки - несоответствие числа или типа элементов, ошибки в идентификатор массива, отсутствие запятых, скобок и т.д. Всё это обрабатывается корректным образом (хотя может стоит подобрать более удачные сообщения об ошибках.)
Но вот где в дереве хранить сам этот массив? В узлах не предусмотрены ссылки на произвольные двоичные данные (BLOB). Можно ввести тип POINTER TO ARRAY OF LONGINT (например), выделить при помощи NEW память нужного размера и разместить там массив. Но потом указатель на этот массив нечему присвоить, узел не содержит поле необходимого типа.
Один из путей - не переделывать узлы, и не выделять сплошной кусок памяти, а распихать байты в имеющиеся поля узла (поля BYTE,INTEGER,LONGINT, OPS.String и т.д.). Но придется писать процедуры "распихивания" и "извлечения" обратно, да и заняты в узле будут только некоторые поля, а остальные болтаться попусту. И к тому же много так не распихаешь-ну около 50 байтов на узел.
Лучше изменить тип ObjDesc или ConstDesc, добавив туда указатель на открытый массив байтов. Единственный недостаток - это поле будет болтаться попусту, если двоичных данных у узла нет. Но один лишний указатель на узел -расход невеликий. К тому же это поле пригодится и в дальнейшем, например для двоичных процедур или чего-то подобного.
Кстати, в ConstDesc уже есть нечто подобное - поле ext типа OPS.String для хранения строчки inline-процедуры (PROCEDURE-).
Новое поле позволит увеличить длину такой процедуры до нескольких строчек.
Так что, думаю, нужно дополнительное поле (назовем, скажем blob, кто-то предложит более удачное?). Вот только куда его лучше вставить? Логичнее в ConstDesc, потому что этот набор данных действительно константы.
Код: "OBERON"
  1.  
  2. Blob* = POINTER TO BlobArray;
  3. BlobArray* = ARRAY OF BYTE;
  4. и соответственно
  5. ConstDesc* = RECORD
  6. ext*: ConstExt; (* string or code for code proc *)
  7. intval*: INTEGER; (* constant value or adr, proc par size, text position or least case label *)
  8. intval2*: INTEGER; (* string length, proc var size or larger case label *)
  9. setval*: SET; (* constant value, procedure body present or "ELSE" present in case *)
  10. realval*: REAL (* real or longreal constant value *)
  11. blob*: Blob;
  12. END ;

Тогда в ConstArray можно будет выделить память NEW(x^.conval^.blob,size);, где size расчитывается из n и размера данных (OPM.IntSize и пр). Хотя лучше создать спецпроцедуру OPB.NewBlob.
Но это будет массив байтов, типы LONGINT и другие надо будет разбить на байты и туда записать, а в OPV при генерации С-программы наоборот из байтов собрать значение. Как наиболее эффективно это сделать, есть ли что-то специально для этого в SYSTEM, кроме DIV, MOD и сдвигов?


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

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


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

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


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

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