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

Твердыня модульных языков
Текущее время: 28 апр 2024, 07:37

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




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

Сообщения: 1019
Откуда: Днепропетровская обл.
Олеж, а как у нас сейчас обрабатывается случай объявления типа массива без размера?
Код: "OBERON"
  1. TYPE TheClocks = ARRAY OF BYTE;
  2. CONST AnimClockData = TheClocks (0040, 00070);
Спрашиваю потому что ещё не тестил твой код и потому что нужно обсудить как будет вообще более правильно реагировать на такое описание: спустить его на тормозах, вычисляя размер массива по количеству заданных значений, либо же ткнуть юзеру, что надо указать размер явно?

Ещё вопрос про многомерные массивы. В текущей реализации можно будет задать такой массив и заполнить его константными строками? Типа вот так:
Код: "OBERON"
  1. TYPE Labirint = ARRAY 3 OF ARRAY 16 OF CHAR;
  2. CONST Map = Labirint("...o..##...oo12", "...o..##...oo35", "...o..##...oo78");


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

Сообщения: 273
Откуда: Россия
Zorko писал(а):
Олеж, а как у нас сейчас обрабатывается случай объявления типа массива без размера?
Код: "OBERON"
  1. TYPE TheClocks = ARRAY OF BYTE;
  2. CONST AnimClockData = TheClocks (0040, 00070);
Спрашиваю потому что ещё не тестил твой код и потому что нужно обсудить как будет вообще более правильно реагировать на такое описание: спустить его на тормозах, вычисляя размер массива по количеству заданных значений, либо же ткнуть юзеру, что надо указать размер явно?
Такой случай я не обрабатывал. Сейчас попробовал - оказалось, в этом случае идентификатор типа просто игнорируется и разбираются дальше числа в скобках. Если, например, это число будет одно, то оно обработается как константа. Это, конечно, ошибка. Проще всего доработать, чтобы в этом случае выдавалась ошибка.

Цитата:
Ещё вопрос про многомерные массивы. В текущей реализации можно будет задать такой массив и заполнить его константными строками? Типа вот так:
Код: "OBERON"
  1. TYPE Labirint = ARRAY 3 OF ARRAY 16 OF CHAR;
  2. CONST Map = Labirint("...o..##...oo12", "...o..##...oo35", "...o..##...oo78");
Текущая реализация работает только с числами, прочие типы нужно реализовать отдельно.


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

Сообщения: 273
Откуда: Россия
Вот сразу и выявилась ошибка. :(
Если идентификатор Ident в описании CONST Const = Ident ... является идентификатором типа "массив", то дальше обрабатывается набор элементов массива, как и должно быть. А если этот идентификатор- не массив (например другая константа), то идентификатор пропускается и дальше обрабатывается остаток выражения.
Это не так легко исправить, потому что идентификатор может быть квалифицированным. А вызов qualident(x^.obj) двигается дальше по тексту программы, поэтому нельзя просто так вызвать затем ConstExpression(x), т.к. часть выражения уже прочитана. Значит, надо залезать вглубь обработчика выражений, к той точке, где обнаруживается, что это именно константный массив, а не иная конструкция.
Ждите исправления.


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

Сообщения: 273
Откуда: Россия
Saferoll писал(а):
Вот сразу и выявилась ошибка. :(
Если идентификатор Ident в описании CONST Const = Ident ... является идентификатором типа "массив", то дальше обрабатывается набор элементов массива, как и должно быть. А если этот идентификатор- не массив (например другая константа), то идентификатор пропускается и дальше обрабатывается остаток выражения.
Для исправления этой ошибки, проследим, как обработает конструкцию
Код: "OBERON"
  1. CONST AnimClockData = TheClocks(
  2. 00400, 00070, 00000, 00700, 00100, 00380, 00000, 00700, 03008, 00000);
исходный Ofront. Оказывается, в этом случае конструкция попытается обработаться в OPP.Factor как вызов процедуры, что, конечно, приведёт к ошибке. Вот в этот момент и нужно перехватить обработку.

Кстати, это давно известный способ добавления новой синтаксической конструкции в компилятор (или интерпретатор). Найди, где происходит ошибка при обработке новой конструкции и добавь там альтернативу. Это не самый эффективный способ в некоторых случаях, но самый быстрый и лёгкий, если некогда исследовать и переделывать весь транслятор целиком.

До сих пор, определение конст.массива в разделе CONST обрабатывалось при помощи такого трюка: процедура ConstArray(x) создавала узел в виде целой константы, равной количеству элементов в массиве (сам массив "прятался" в полях x.obj и x.conval.arr). Это позволило использовать стандартные процедуры для работы с деревом, до того, как в процедуре Block создавался "константный массив - входной параметр".
Но Factor вызывается везде, где требуется выражение. Поэтому, если не принять специальных мер, то можно будет где-то в программе написать
Код: "OBERON"
  1. i := 3*TheClocks(
  2. 00400, 00070, 00000, 00700, 00100, 00380, 00000, 00700, 03008, 00000)+1
, в результате чего целой переменной i будет присвоено значение 31.
Конечно же, это недопустимо. поэтому изменим тип создаваемой константы - путь создаётся константа NIL, она более ограничена. Но NIL можно сравнивать (= и # ), поэтому дополнительно изменим тип на undftyp, с которым невозможны никакие операции. В качестве дополнительной меры проконтролируем, что после закрывающей скобки стоит ";".
Код: "OBERON"
  1. PROCEDURE ConstArray (VAR x: OPT.Node);
  2. (* конструкция "константный массив" *)
  3. VAR apar: OPT.Node; n,size,i: INTEGER; fp: OPT.Object; y: OPT.ConstArray;
  4. (* переменные для создания конс.массива при помощи NEW *)
  5. arrByte: OPT.ConstArrayOfByte; arrSInt: OPT.ConstArrayOfSInt;
  6. arrInt: OPT.ConstArrayOfInt;
  7. BEGIN
  8. IF x^.obj^.typ^.BaseTyp^.form IN intSet THEN (* массив из целых (в т.ч. BYTE) *)
  9. n:=x^.obj^.typ^.n; (* количество элементов массива *)
  10. CheckSym(lparen); (* скобка ( *)
  11. fp := OPT.NewObj(); fp^.typ := x^.obj^.typ^.BaseTyp;
  12. fp^.mode := Var; (* fp - переменная, элемент массива *)
  13. NEW(y);
  14. (* тут нужно выделить кусок памяти для n элементов массива *)
  15. CASE fp^.typ^.size OF (* Размер элементов массива в байтах (для BlackBox). *)
  16. | 1: (* BOOLEAN, CHAR, SYSTEM.BYTE (для Ofront'а) *)
  17. NEW(arrByte); NEW(arrByte.val, n); y := arrByte;
  18. | 2: (* типы с размером в 2 байта *)
  19. NEW(arrSInt); NEW(arrSInt.val, n); y := arrSInt;
  20. | 4: (* типы с размером в 4 байта *)
  21. NEW(arrInt); NEW(arrInt.val, n); y := arrInt;
  22. | 8: (* 64-разрядных типов пока нет *)
  23. END;
  24. IF sym # rparen THEN
  25. i := 0;
  26. LOOP ConstExpression(apar); (* берем очередной элемент *)
  27. IF i < n THEN
  28. OPB.Param(apar, fp); (* проверим на совместимость*)
  29. (* тут нужно положить элемент в кусок памяти *)
  30. WITH
  31. | y : OPT.ConstArrayOfByte DO (* BOOLEAN, CHAR, SYSTEM.BYTE (для Ofront'а) *)
  32. y.val[i] := SHORT(SHORT(apar^.conval^.intval));
  33. | y : OPT.ConstArrayOfSInt DO (* типы с размером в 2 байта *)
  34. y.val[i] := SHORT(apar^.conval^.intval);
  35. | y : OPT.ConstArrayOfInt DO (* типы с размером в 4 байта *)
  36. y.val[i] := apar^.conval^.intval;
  37. END;
  38. INC(i);
  39. ELSE err(64)
  40. END ;
  41. IF sym = comma THEN OPS.Get(sym)
  42. ELSIF (lparen <= sym) & (sym <= ident) THEN err(comma)
  43. ELSE EXIT
  44. END
  45. END;
  46. END;
  47. (* x - тип массив, y -элементы массива в памяти, *)
  48. fp := x^.obj; (* запомним тип "массив"*)
  49. x := OPB.Nil(); (* как будто это NIL *)
  50. x^.typ := OPT.undftyp; (* чтобы никаких операций с конструкцией нельзя было выполнять *)
  51. x^.conval^.intval := n; (* количество элементов массива *)
  52. x^.conval^.arr := y; (* сами элементы *)
  53. x^.obj := fp; (* константа с типом "массив" *)
  54. IF i # n THEN err(65) END;
  55. CheckSym(rparen);
  56. ELSE err(51)
  57. END;
  58. END ConstArray;
  59.  
  60. PROCEDURE Factor(VAR x: OPT.Node);
  61. VAR fpar, id: OPT.Object; apar: OPT.Node;
  62. BEGIN
  63. IF sym < lparen THEN err(13);
  64. REPEAT OPS.Get(sym) UNTIL sym >= lparen
  65. END ;
  66. IF sym = ident THEN
  67. qualident(id); x := OPB.NewLeaf(id); selector(x);
  68. IF (x^.class = Nproc) & (x^.obj^.mode = SProc) THEN StandProcCall(x) (* x may be NIL *)
  69. ELSIF sym = lparen THEN
  70. IF (x^.obj^.mode = Typ) & (x^.obj^.typ^.form = Comp) & (x^.obj^.typ^.comp = Array) THEN
  71. (* константный массив *)
  72. ConstArray(x);
  73. IF sym # semicolon THEN err (semicolon) END; (* на всякий случай проверим, что в конце ; *)
  74. ELSE
  75. ...
  76.  
Вот такой узел благополучно проходит через обработчик выражений в раздел OPP.Block, занимающийся секцией CONST.
А там он опознаётся по признаку conval^.arr # NIL и преобразуется в "массив - входной параметр" (VarPar, inPar), с которым уже можно работать, как будто он поступил откуда-то извне через IN-параметр процедуры. Но сама конструкция TheClocks(...) для прочих частей Ofront воспринимается как выражение неопределённого типа, что вызывает уместные ошибки навроде "operand type inapplicable".
Код: "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.  
  7. BEGIN first := NIL; last := NIL; nofFwdPtr := 0;
  8. LOOP
  9. IF sym = const THEN
  10. OPS.Get(sym);
  11. WHILE sym = ident DO
  12. OPT.Insert(OPS.name, obj); CheckMark(obj^.vis);
  13. obj^.typ := OPT.sinttyp; obj^.mode := Var; (* Var to avoid recursive definition *)
  14. IF sym = eql THEN
  15. OPS.Get(sym);
  16. ConstExpression(x)
  17. ELSIF sym = becomes THEN
  18. err(eql); OPS.Get(sym); ConstExpression(x)
  19. ELSE err(eql); x := OPB.NewIntConst(1)
  20. END ;
  21. obj^.mode := Con; obj^.typ := x^.typ; obj^.conval := x^.conval; (* ConstDesc ist not copied *)
  22. IF obj^.conval^.arr # NIL THEN (* константный массив*)
  23. obj^.mode := VarPar; (* преобразуем в переменную-параметр *)
  24. obj^.vis := inPar; (* причём входной параметр*)
  25. IF x^.obj # NIL THEN obj^.typ := x^.obj^.typ END;
  26. obj^.typ^.pvused := TRUE; (* не знаю, нужно ли это *)
  27. IF last = NIL THEN OPT.topScope^.scope := obj ELSE last^.link := obj END ;
  28. last := obj;
  29. first := NIL;
  30. END;
  31. CheckSym(semicolon)
  32. END
  33. END;


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

Сообщения: 273
Откуда: Россия
Массив без размера
Zorko писал(а):
Олеж, а как у нас сейчас обрабатывается случай объявления типа массива без размера?
Код: "OBERON"
  1. TYPE TheClocks = ARRAY OF BYTE;
  2. CONST AnimClockData = TheClocks (0040, 00070);

Спрашиваю потому что ещё не тестил твой код и потому что нужно обсудить как будет вообще более правильно реагировать на такое описание: спустить его на тормозах, вычисляя размер массива по количеству заданных значений, либо же ткнуть юзеру, что надо указать размер явно?

Мы уже обсуждали что-то подобное и сошлись, вроде бы, на том, что явное указание количества байтов, слов или каких-то иных элементов в структуре данных чаще всего полезно, т.к. вводит дополнительный контроль.
Например, пусть у нас в игре 3 комнаты, данные для которых задаются как
Код: "OBERON"
  1. CONST MaxRoom = 3;
  2. TYPE Rooms = ARRAY MaxRoom*6*8 OF BYTE;
  3. CONST RoomData = Rooms
  4. ( 3,12,0,0,5,2,1,100, (* Room 1*)
  5. 33,14,0,0,15,2,1,10,
  6. 3,12,0,0,5,2,1,100,
  7. 33,14,0,0,15,2,1,10,
  8. 3,12,0,0,5,2,1,100,
  9. 33,14,0,0,15,2,1,10,
  10. ... (* Room 2*)
  11.  
  12. ... (* Room 3*)
  13. );
Теперь, чтобы добавить еще одну комнату, необходимо изменить CONST MaxRoom = 4; и добавить числа в конец массива RoomData. Если мы добавим данные для комнаты, а константу изменить забудем или наоборот (константу изменим, а байты не добавим или какое-то число пропустим), то компилятор это заметит и выдаст ошибку.
И очень хорошо, что есть такой контроль. Да и контроль не слишком обременителен, потому что константы типа MaxRoom всё равно,обычно, где-то в программе используются.
Но это хорошо для данных, где каждый элемент имеет одинаковую длину. Что будет, если данные для комнаты состоят из разного числа байтов?
Код: "OBERON"
  1. CONST MaxRoom = 3;
  2. LenRoom1 = 6*8;
  3. LenRoom2 = 6*8+5;
  4. LenRoom3 = 3*7+10;
  5. LenRoomData = LenRoom1+LenRoom2+LenRoom3;
  6. TYPE Rooms = ARRAY LenRoomData OF BYTE;
  7. CONST RoomData = Rooms
  8. ( 3,12,0,0,5,2,1,100, (* Room 1*)
  9. 33,14,0,0,15,2,1,10,
  10. 3,12,0,0,5,2,1,100,
  11. 33,14,0,0,15,2,1,10,
  12. 3,12,0,0,5,2,1,100,
  13. 33,14,0,0,15,2,1,10,
  14. ... (* Room 2*)
  15.  
  16. ... (* Room 3*)
  17. );
Тут нужно кроме изменения константы MaxRoom и добавления байтов в массив RoomData ещё ввести новую константу LenRoomX, и записать её в сумму LenRoomData. Какой-то контроль и тут сохраняется - компилятор заметит, если мы добавим байты в массив ,но забудем изменить LenRoomData. Однако, стало труднее, потому что необходимо подсчитать число байтов и изменить программу в нескольких местах.
А если бы можно было не указывать количество байтов и написать просто TYPE Rooms = ARRAY OF BYTE;? В этом случае компилятор не сможет проконтролировать правильность количества данных и должен полагаться на программиста. Но зато не заставит их пересчитывать.
И это тоже мы обсуждали и согласились, что иногда контроль ничего хорошего не даёт и удобство важнее.
Заметим, что в обоих случаях количество элементов самого константного массива CONST RoomData = Rooms (31, 7, 123, ...); задано количеством константных выражений в скобках. Просто в одном случае оно же задаётся дополнительно и в описании массивового типа, поэтому у компилятора есть возможность проконтролировать соответствие.
Поэтому, думаю, возможность опускать размер массива будет полезна. Ведь никакой современный компилятор ЯВУ (и даже ассемблер) не заставляет для строковой константы явно указывать её длину -мы просто перечисляем символы в кавычках. А если бы нужно было писать 5"Hello" или 0"", насколько бы это стало сложнее!
Если размер каждого элемента фиксирован и программисту не составит труда его указать, то лучше указать в типе явно. А если длина элементов плавает или неизвестна, а пересчитывать пальцем лень, то можно и не указывать.
Конечно, кто-то не будет указывать никогда, но это уже вопрос стиля и грамотности.

По реализации. Пока возможность опускать указание размера не сделана и обрабатывается как ошибка. Для реализации нужно решить вопрос с хранением элементов. Если массив имеет фиксированный размер, то мы сначала выделяем один кусок памяти для хранения всех его элементов, а затем в процессе чтения заполняем. Если размер заранее неизвестен, то придется выделять память связанными в список кусками некоторого стандартного размера (на 100, 1024 или 4*1024 элемента, например). А если очередной кусок кончился, то выделить следующий и подсоединить его к списку. Размер куска (лучше наверно задавать его не в количестве элементов, а в КБ) можно установить в константах Ofront или даже в par-файле.


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

Сообщения: 1019
Откуда: Днепропетровская обл.
Можно решить эту задачу тем же способом, который я применил для убирания лимита на размер кодовых процедур в BlackBox. Вместо списка записей с указателем на следующую я применил динамический массив. Начальный размер массива можно сделать достаточно ёмким для мелких нужд — например, 4 кб. В случае когда размера не хватает применяется резервирование нового массива двойной от изначальной (что тоже можно варьировать) длины и копирование элементов из старого в новый.

Вот фрагмент из процедуры CPP.ProcedureDeclaration.GetCode, показывающий данный способ:
Код: "OBERON"
  1. VAR s, s2: POINTER TO ARRAY OF SHORTCHAR;
  2. ...
  3. NEW(s, 256); (* Здесь 256 байт, но для конст. массивов нужно больше *)
  4. LOOP
  5. IF sym = number THEN c := DevCPS.intval; INC(n); i := LEN(s^);
  6. IF n > i THEN (* if overflow then increase size of array s *)
  7. (* Удвоение размера массива и копирование элементов *)
  8. NEW(s2, i * 2); WHILE i > 0 DO DEC(i); s2^[i] := s^[i] END; s := s2; s2 := NIL;
  9. (* На старый массив не осталось указателей и он будет убран сборщиком мусора *)
  10. END;
  11. ...
Здесь есть конечно оверхед — накладные расходы на копирование элементов, однако оверхед ступенчатый: если массив до 4 кб, никакого оверхеда; если ненамного больше — он минимальный; в случае огромного массива — довольно приемлемый. При начальном значении в 4 кб и удвоении размера оверхед на мегабайтный массив будет 8 перебросок данных. 4 мб — 10 перебросок. Вариант со списком имеет свои преимущества, но несколько сложнее в реализации.


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

Сообщения: 273
Откуда: Россия
Zorko писал(а):
Можно решить эту задачу тем же способом, который я применил для убирания лимита на размер кодовых процедур в BlackBox. Вместо списка записей с указателем на следующую я применил динамический массив. Начальный размер массива можно сделать достаточно ёмким для мелких нужд — например, 4 кб. В случае когда размера не хватает применяется резервирование нового массива двойной от изначальной (что тоже можно варьировать) длины и копирование элементов из старого в новый.
Да, этот способ проще, спасибо. Кажется, в Turbo Vision применялось что-то подобное. Можно пока сделать так, а потом, если будет время и желание, можно будет переписать в виде списка или еще как-то.


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

Сообщения: 273
Откуда: Россия
Zorko писал(а):
Ещё вопрос про многомерные массивы. В текущей реализации можно будет задать такой массив и заполнить его константными строками? Типа вот так:
Код: "OBERON"
  1. TYPE Labirint = ARRAY 3 OF ARRAY 16 OF CHAR;
  2. CONST Map = Labirint("...o..##...oo12", "...o..##...oo35", "...o..##...oo78");
Сначала про типы элементов константного массива. Сейчас реализованы только массивы из целых чисел (в том числе обрабатывается и BYTE). Причём из-за совместимости байтового типа с символьным элементы массива можно задавать например так
Код: "OBERON"
  1. TYPE TheClocks = ARRAY 5 OF BYTE;
  2. CONST AnimClockData = TheClocks(41,'L', "W", 0F2X, CHR(0F2H));
  3. (* А вот 0F2H не проходит, потому что лежит вне диапазона -128..127 *)
И это сделано не специально для константных массивов, это следствие «странностей» байтового типа, который нарочно был предназначен для нарушения строгой типизации в системных целях.
Можно добавить константные массивы для значений любых типов, которые допустимы для обычных констант. Для этого необходимо расширить тип ConstArr полем val нужного типа, а в процедуру ConstArray добавить ветви для работы с этим полем.

А вот теперь про многомерные массивы. Обычно, всякая новая фича проходит через стадии «0-1-бесконечность», т.е. сначала нет такого вообще, потом появляется в единственном экземпляре, а если можно один, так почему не много?
Сейчас константные массивы могут быть только одномерные. Многомерный массив – это одномерный массив из массивов, которые тоже могут состоять из массивов или элементов простого типа. Какой синтаксис при этом мы должны использовать?
Например, достаточно ли удачна конструкция
Код: "OBERON"
  1. TYPE Board = ARRAY 3,4 OF INTEGER;
  2. CONST BoardData = Board((11,12,13,14), (21,22,23,24), (31,32,33,34));
Сейчас процедура OPP.Factor вызывает ConstArr, если обнаружит попытку вызвать тип как процедуру. Но вот обработка подмассивов должна происходить уже по-другому, потому что перед скобками нет идентификатора типа. Если не внести изменений, то произойдет ошибка при чтении запятой после первого числа в скобках. Можно тут перехватить ошибку и рассматривать элементы в скобках как значение особого типа.
Правда, сразу же возникает двусмысленность с массивами единичной длины. Синтаксически такие массивы возможны, хотя и непонятно, что это даёт на практике. Но (5) это что? Массив из одного элемента или просто константное выражение? В Питоне в этом случае требуется писать лишнюю запятую
Код: "OBERON"
Значит, здесь потребуется не просто чтение константного значения, а чтение именно «конструкции константный массив». Т.е. надо заранее проконтролировать наличие открывающейся скобки, прочитать элемент, затем убедиться, что есть «)». Но не будет ли путаницы, если массив динамический, т.е. количество элементов не указано? Не спутаем ли мы в этом случае при подсчете "(число)" с массивом единичной длины?
Ещё одна сложность – строки. Трудность в том, что строки это массив из символов, что нужно соответствующим образом обработать.
И я правильно понимаю, что в константных массивах все строки должны быть одинаковой длины (хотя эта длина может быть явно не указана, а определяться количеством символов в кавычках)?

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


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

Сообщения: 273
Откуда: Россия
Saferoll писал(а):
Значит, здесь потребуется не просто чтение константного значения, а чтение именно «конструкции константный массив». Т.е. надо заранее проконтролировать наличие открывающейся скобки, прочитать элемент, затем убедиться, что есть «)». Но не будет ли путаницы, если массив динамический, т.е. количество элементов не указано? Не спутаем ли мы в этом случае при подсчете "(число)" с массивом единичной длины?
Думаю путаницы не будет, потому что структура всегда известна, даже если неизвестен размер. Мы знаем, что сейчас должен быть массив, значит возьмём из текста символ "(" и обрабатываем рекурсивно конструкцию "элементы через запятую", пока не наткнемся на лишнюю ")". Т.е. она не лишняя, она соответствует скобке, которую мы взяли в начале обработки. Значит массив закончился и нужно обрабатывать дальше.
Если размер массива в типе был указан, то дополнительно сверим его с количеством прочитанных элементов. Если не указан (описан открытый массив), то примем за этот размер просто количество элементов.
Массивы единичной длины тоже правильно обработаются, потому что тоже заключены в скобки и эти скобки мы не перепутаем, т.к. знаем ,что тут должен быть массив. В Питоне требуется писать "(5,)", потому что тип заранее неизвестен и выражение может быть и числом и кортежом, так что надо уметь узнавать тип по самому выражению. Уточнение: вернее, в Питоне кортеж образуется при помощи запятой, а скобки просто отделяют созданное от соседних членов выражения.
У нас же нет необходимости требовать наличие запятой, потому что в конструкции CONST AnimClockData = TheClocks (0040, 00070); тип всегда указан перед скобками.


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

Сообщения: 273
Откуда: Россия
Попробуем сделать константные многомерные массивы. Когда в процедуре OPP.Block будет обнаружен идентификатор типа после = в разделе CONST, создадим новый узел для размещения сразу всех элементов многомерного массива, как будто это был одномерный массив. Для этого служит процедура OPB.NewArrConst
Код: "OBERON"
  1. PROCEDURE NewArrConst*(VAR x:OPT.Node);
  2. (* подготовить узел для константного массива *)
  3. VAR n: INTEGER; typ: OPT.Struct;y: OPT.ConstArr;
  4. fp: OPT.Object;
  5. arrByte: OPT.ConstArrOfByte; arrSInt: OPT.ConstArrOfSInt;
  6. arrInt: OPT.ConstArrOfInt;
  7. BEGIN
  8. n := 1;
  9. typ := x^.typ;
  10. WHILE (typ^.form =15) & (typ^.comp = 2) DO
  11. n := n * typ^.n; (* перемножение размеров по всем измерениям *)
  12. typ := typ^.BaseTyp;
  13. END;
  14. (* n - общее кол-во элементов массива, typ - тип элемента *)
  15. IF typ^.form IN intSet THEN (* массив из целых (в т.ч. BYTE) *)
  16. fp := OPT.NewObj(); fp^.typ := x^.obj^.typ^.BaseTyp;
  17. fp^.mode := Var; (* fp - переменная, элемент массива *)
  18. NEW(y);
  19. (* тут нужно выделить кусок памяти для n элементов массива *)
  20. CASE typ^.size OF (* Размер элементов массива в байтах (для BlackBox). *)
  21. | 1: (* BOOLEAN, CHAR, SYSTEM.BYTE (для Ofront'а) *)
  22. NEW(arrByte); NEW(arrByte.val, n); y := arrByte;
  23. | 2: (* типы с размером в 2 байта *)
  24. NEW(arrSInt); NEW(arrSInt.val, n); y := arrSInt;
  25. | 4: (* типы с размером в 4 байта *)
  26. NEW(arrInt); NEW(arrInt.val, n); y := arrInt;
  27. | 8: (* 64-разрядных типов пока нет *)
  28. END;
  29. fp := x^.obj; (* запомним тип "массив"*)
  30. x := Nil(); (* как будто это NIL *)
  31. x^.typ := OPT.undftyp; (* чтобы никаких операций с конструкцией нельзя было выполнять *)
  32. x^.conval^.intval := 0; (* количество элементов массива уже заполненных*)
  33. x^.conval^.intval2 := n; (* сколько всего д б элементов массива *)
  34. x^.conval^.arr := y; (* сами элементы *)
  35. x^.obj := fp; (* константа с типом "массив" *)
  36. ELSE err(51)
  37. END;
  38. END NewArrConst;
А вот заполнять этот массив будем рекурсивным вызовом OPP.ConstArray
Код: "OBERON"
  1. PROCEDURE ConstArray (VAR x: OPT.Node; typ: OPT.Struct);
  2. (* конструкция "константный массив". typ - текущий уровень массива *)
  3. VAR apar: OPT.Node; n, i: INTEGER; fp: OPT.Object; y: OPT.ConstArr;
  4. BEGIN
  5. CheckSym(lparen); (* скобка ( *)
  6. n:=typ^.n; (* количество элементов массива *)
  7. i := 0;
  8. IF typ^.BaseTyp^.form IN intSet THEN (* массив из целых (в т.ч. BYTE) *)
  9. y := x^.conval^.arr;
  10. fp := OPT.NewObj(); fp^.typ := typ^.BaseTyp;
  11. fp^.mode := Var; (* fp - переменная, элемент массива *)
  12. IF sym # rparen THEN
  13. LOOP ConstExpression(apar); (* берем очередной элемент *)
  14. IF i < n THEN
  15. OPB.Param(apar, fp); (* проверим на совместимость*)
  16. (* тут нужно положить элемент в кусок памяти *)
  17. WITH
  18. | y : OPT.ConstArrOfByte DO (* BOOLEAN, CHAR, SYSTEM.BYTE (для Ofront'а) *)
  19. y.val[x^.conval^.intval+i] := SHORT(SHORT(apar^.conval^.intval));
  20. | y : OPT.ConstArrOfSInt DO (* типы с размером в 2 байта *)
  21. y.val[x^.conval^.intval+i] := SHORT(apar^.conval^.intval);
  22. | y : OPT.ConstArrOfInt DO (* типы с размером в 4 байта *)
  23. y.val[x^.conval^.intval+i] := apar^.conval^.intval;
  24. END;
  25. INC(i);
  26. ELSE err(64)
  27. END ;
  28. IF sym = comma THEN OPS.Get(sym)
  29. ELSIF (lparen <= sym) & (sym <= ident) THEN err(comma)
  30. ELSE EXIT
  31. END
  32. END;
  33. END;
  34. IF i # n THEN err(65) END;
  35. INC(x^.conval^.intval, n); (* учли прочитанные элементы, даже если они не прочитаны *)
  36. ELSIF (typ^.BaseTyp^.form =15) & (typ^.BaseTyp^.comp = 2) THEN (* массив из массивов *)
  37. IF sym # rparen THEN
  38. LOOP ConstArray (x, typ^.BaseTyp); (* рекурсивная обработка подмассива *)
  39. IF i < n THEN
  40. (* ??? проверим на совместимость *)
  41. INC(i);
  42. ELSE err(64)
  43. END ;
  44. IF sym = comma THEN OPS.Get(sym)
  45. ELSIF (lparen <= sym) & (sym <= ident) THEN err(comma)
  46. ELSE EXIT
  47. END
  48. END;
  49. END;
  50. IF i # n THEN err(65) END;
  51. ELSE err(51)
  52. END;
  53. CheckSym(rparen); (* скобка ) *)
  54. END ConstArray;
Эта процедура обрабатывает конструкцию "выражения в скобках через запятую", где каждое выражение либо обычное константное выражение, либо тоже "выражения в скобках через запятую". При этом количество представленных элементов сравнивается с количеством, указанным в типе массива (при несовпадении вставляется маркер ошибок). Не знаю, нужна ли еще какая-то дополнительная проверка в месте (* ??? проверим на совместимость *).
В процедуре OPP.Factor выражение "Тип(элементы через запятую)" будет обрабатываться так
Код: "OBERON"
  1. PROCEDURE Factor(VAR x: OPT.Node);
  2. VAR fpar, id: OPT.Object; apar: OPT.Node;
  3. BEGIN
  4. IF sym < lparen THEN err(13);
  5. REPEAT OPS.Get(sym) UNTIL sym >= lparen
  6. END;
  7. IF sym = ident THEN
  8. qualident(id); x := OPB.NewLeaf(id); selector(x);
  9. IF (x^.class = Nproc) & (x^.obj^.mode = SProc) THEN StandProcCall(x) (* x may be NIL *)
  10. ELSIF sym = lparen THEN
  11. IF (x^.obj^.mode = Typ) & (x^.obj^.typ^.form = Comp) & (x^.obj^.typ^.comp = Array) THEN
  12. (* константный массив верхнего уровня*)
  13. OPB.NewArrConst(x);
  14. ConstArray(x, x^.obj^.typ);
  15. IF sym # semicolon THEN err (semicolon) END; (* на всякий случай проверим, что в конце ; *)
  16. ELSE
  17. OPS.Get(sym); OPB.PrepCall(x, fpar);
  18. ActualParameters(apar, fpar);
  19. OPB.Call(x, apar, fpar);
  20. CheckSym(rparen);
  21. END;
А процедура OPP.Block изменений не потребует.
Вот теперь верно (?) строится узел "константный массив" для массивов любого числа измерений. Но должен быть указан конкретный размер по каждому измерению, кроме того, массив должен быть целочисленным.
Обработка многомерных массивов при генерации С-исходника пока не сделана, массив там генерируется как одномерный.


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

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


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

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


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

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