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

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

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




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

Сообщения: 189
Всем привет!
Начинаю разработку тестового компилятора совместимого с Оберон 07 но расширенного до определённых размеров!
Постараюсь учесть все нюансы (FOR, унарный минус, аспекты константных массивов и т.д.)
Для начала, как инструмент разработки, возьму FreePascal последней версии и максимально настрою его на работу в стиле Оберон...
Во вложении заготовка для экспериментов, опишу некоторые файлы.

Файл RTL.mod - это эмулятор системных функций Оберонов
Код: "OBERON"
  1.  
  2. FUNCTION _DIV(CONST x, y: INT64): INT64;
  3. FUNCTION _MOD(CONST x, y: INT64): INT64;
  4. FUNCTION _ASH (e, bit : INTEGER) : INTEGER;
  5. FUNCTION _LSL(CONST x, y: INTEGER): INTEGER;
  6. FUNCTION _ASR(CONST x, y: INTEGER): INTEGER;
  7. FUNCTION _ROR(CONST x, y: INTEGER): INTEGER;
  8. FUNCTION _FLT(CONST i: INTEGER) : SINGLE;
  9. FUNCTION _ITR(CONST i: INTEGER) : SINGLE;
  10. FUNCTION _RTI(CONST r: SINGLE) : INTEGER;
  11. FUNCTION _HTR(CONST i: INT64) : DOUBLE;
  12. FUNCTION _RTH(CONST r: DOUBLE) : INT64;
  13. FUNCTION _AND(CONST x, y: INTEGER): INTEGER;
  14. FUNCTION _HASH(p: POINTER; l: INTEGER): INTEGER;
  15.  

По моему тут все понятно и в коментариях не нуждается, однако оговорюсь, для пользователей, которые ещё пока не достаточно профессионально работают в Оберонах:
- Это реализация внутренних функций и операторов компилятора, так что можно посмотреть как работает та или иная директива.

Файл MCS.mod - это лексический анализатор, его пока не будем изучать, оставим на потом...

Далее файл MCB.mod - это уже файл компилятора с начальными настройками.
Именно он инициализирует настройки компилятора, создаёт базовые файлы и т.д. А так же включает в себя процедуры работы с деревом разбора синтаксических конструкций.
Вот на нём и остановимся подробно!

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

Код: "OBERON"
  1.  
  2. Type_ = CLASS
  3. ...
  4. END;
  5.  
  6. Item = CLASS
  7. ...
  8. END;
  9.  
  10. Object_ = CLASS (Item)
  11. ...
  12. END;
  13.  

Как видно, Item реперь не RECORD (запись) а POINTER т.е. указатель, в связи с этим изменится и поведение нашего компилятора!

Две функции:
Код: "OBERON"
  1.  
  2. PROCEDURE Enter(name: ARRAY OF CHAR; clas: INTEGER; typ: Type_; val: LONGINT);
  3. PROCEDURE Create(VAR x: Type_; form: INTEGER; size: LONGINT);
  4.  

Отвечают за инициализацию базовых типов и привязывают объекты именованые к ним, НО!!!!! посмотрите как они добавлябтся в дерево, не в конец списка, а в начало
Код: "OBERON"
  1.  
  2. PROCEDURE Enter(name: ARRAY OF CHAR; clas: INTEGER; typ: Type_; val: LONGINT);
  3. VAR
  4. x: Object_;
  5. BEGIN
  6. ...
  7. x.c := hidden;
  8. hidden := x;
  9. ...
  10. END;
  11.  


И наконец 3 функции отвечающие за видимость объектов (это же важно... что бы вложенные структуры видели все предыдущие объекты, но были недоступны на предыдущих уровнях. Так вот, функции:
Код: "OBERON"
  1.  
  2. PROCEDURE OpenScope;
  3. PROCEDURE CloseScope;
  4. PROCEDURE NewObj(VAR x: Object_; id: Ident; clas: INTEGER);
  5.  

как раз за это и отвечают...

Процедуры OpenScope и CloseScope отвечают за переключение уровней видимости
Код: "OBERON"
  1.  
  2. x.dsc := topScope;
  3. x.c := NIL;
  4. topScope := x;
  5. ...
  6. topScope := topScope.dsc
  7.  

Для каждого объекта может быть создано поле .dsc в который и попадут внутренние объекты, например параметры процедур, локальные переменные, типы и константы
Во вложении маленькая программка которая и отображает выше-описаное, более того, это заготовка для экспортирования наших данных в файл экспорта!!!

Пример как она работает чисто визуальный:
Код: "OBERON"
  1.  
  2. INTEGER
  3. BOOLEAN
  4. O0
  5. O1
  6. O2
  7. O3
  8. O4
  9. O5
  10. O6
  11. O7
  12. O8
  13.  

Видим, что глобальная видимость у певых 4-х веточек дерева, и на этом уровне не будут видны следующие...

Ну вот пока всё, в следующий раз подробно остановимся на константах, и нисходящем разборе выражений!


Вложения:
part1.rar [721.44 КБ]
Скачиваний: 325
Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 26 ноя 2018, 16:54 
Не в сети

Сообщения: 146
S.Atan писал(а):
Как известно, Н.В. Вирт в своих компиляторах идёт двумя путями
1) Создаётся код, на прямую пишущийся в файл (большинство)
2) Создаётся дерево синтаксическое, а затем, если что то надо изменить, переместить и т.д. оно модифицируется и транслируется.
Интересно узнать, в каких своих компиляторах Вирт применял 2-й подход?
Код: "OBERON"
Ничего себе микроб.


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

Сообщения: 189
Comdiv писал(а):
Ничего себе микроб.

Ну... я не обещал что используемый инструмент выдаст маленький файл...
На Java (иже с ней) или NET пришлось бы тащить около сотни Мб... ;)

Второй подход Вирт использует в Lola2 компиляторе.


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

Сообщения: 67
Откуда: Equestria
S.Atan писал(а):
Для начала, как инструмент разработки, возьму FreePascal последней версии и максимально настрою его на работу в стиле Оберон...
Я вот жалею что написал свой компилятор на сишке, а не сразу на обероне. Бросил его потому что ниасилил раскручивать, считай сделать всё заново. Так что хорошенько продумай сей момент, пока не зашёл слишком далеко.
S.Atan писал(а):
Как видно, Item реперь не RECORD (запись) а POINTER
Выглядит достаточно странно. Если речь идёт о AST, то такие структуры называются Node и они никак не зависят от целевого процессора. Item же объект временный и живёт он в компиляторе ровно столько, сколько и нечто положенное на стек целевого процессора в рантайме, делать его динамическим нет смысла. Регистры для Item выделяются и освобождаются строго по принципу lifo, это важно.
Расширение Object от Item - тоже нечто странное. Это вообще разные штуки.

Рекомендую ещё раз почитать книжку Project Oberon 2013 и исходники других компиляторов с AST (OP2, BlackBox, Ofront и т.д.). Обрати внимание на структуры данных и способы их применения.


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

Сообщения: 189
Паскаль и Оберон синтаксически очень схожие языки, поэтому трудностей не возникает, более того на FreePascal я как раз и эмулирую работу Оберона (NEW и Garbage Collection) 64-128 бит во FPC думаю помогут...

А на счет Item - Node тут в принципе постулаты не важны (Item и есть временное хранилище для Object, а он её расширение :shock: :D ), более того, сам мэтр иногда плюёт на постулаты и оставляет логику за собой...
Поступим и мы так же по хулигански ;)

N.Wirth (*Lola System Compiler Base*) LSB.mod:
Код: "OBERON"
  1.  
  2. ItemDesc* = RECORD
  3. tag*: INTEGER;
  4. ...
  5. END ;
  6.  
  7. ObjDesc* = RECORD (ItemDesc)
  8. next*: Object;
  9. ...
  10. END ;


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

Сообщения: 189
Немножко остановлюсь на процедурах
Код: "OBERON"
  1.  
  2. PROCEDURE NewObj(VAR x: Object_; id: Ident; clas: INTEGER);
  3. PROCEDURE Enter(name: ARRAY OF CHAR; clas: INTEGER; typ: Type_; val: LONGINT);
  4.  

Первая создаёт объект и добавляет его к нашему дереву в конец списка, вторая создаёт объект для стандартного типа и добавляет его в начало.
То есть дерево наше делится на два участка, один как бы растёт в конце, второй растёт в верх (условно)
Код: "OBERON"
  1.  
  2. ^ BOOLEAN
  3. ^ INTEGER
  4. ------ hidden (* Это метка для быстрого доступа к верхней ветке *)
  5. | O1
  6. | O2
  7. | ...
  8.  

Естественно, при парсинге модуля, мы процедурой OpenScope скроем стандартные объекты (типы и функции псевдомодуля SYSTEM) и у нас при завершении разбора останется упорядоченое дерево, в котором легко ориентироватся, а если мы введём ещё и импортирование, то дерево на самом верхнем уровне будет выгядеть как то так
Код: "OBERON"
  1.  
  2. ^ BOOLEAN
  3. ^ INTEGER
  4. ------ hidden (* Это метка для быстрого доступа к верхней ветке *)
  5. | Module1
  6. | O1
  7. | O2
  8. | O3
  9. | O4
  10. | O5
  11. | O6
  12. | Module2
  13. | O1
  14. | O2
  15. | ...
  16.  

При чем, объекты с одинаковыми именами, но в разных модулях не будут видеть друг друга, но будут видеть объекты предка, то есть стандартны типы и процедуры
Код: "OBERON"
  1.  
  2. ^ BOOLEAN
  3. ^ INTEGER
  4. ------ hidden (* Это метка для быстрого доступа к верхней ветке *)
  5. | Module1
  6. | O1 (* Не видит Module2.O1*)
  7. ...
  8. | Module2
  9. | O1 (* Не видит Module1.O1*)
  10. | ...
  11.  

Поэтому упрощается поиск, но при необходимости (объект помечен для экспорта) мы с лёгкостью получим доступ к полям модуля.

Возникают два вопроса:
1) Поиск в дереве идёт прямым перебором всех полей
2) Раз дерево и так классное, зачем структура Item обозначена как POINTER TO RECORD, а не как RECORD, что не значительно, но упрощает компилятор

Первое описано в трудах Вирта о компьютерах за (ну... уже почти век) время работы его с ними, и показано, что вместо оптимизации процессоров, производители наращивают мощностя и оставляют совместимость, поэтому прямой поиск и оптимизированый практически не отличаются по времени при малых величинах, но упрощает компилятор и понимание кода!
Вот примеры поиска Оберон 07 (MCB.ThisObj) и PO2, BlackBox (OPT.FindInScope)
Код: "OBERON"
  1.  
  2. FUNCTION ThisObj(id: Ident): Object_;
  3. VAR
  4. s, x: Object_;
  5. BEGIN
  6. s := topScope;
  7. REPEAT
  8. x := s.c;
  9. WHILE (x <> NIL) AND (x.name <> id) DO x := x.c;
  10. s := s.dsc;
  11. UNTIL (x <> NIL) OR (s = NIL);
  12. RESULT := x;
  13. END;
  14.  

Это как раз о поиске в дереве, перебираются глобальные видимые объекты (в данном случае модули, и если объект найден по имени, переключаемся на внутреннее дерево объекта (*.dsc) и ищем в нём...
Код: "OBERON"
  1.  
  2. PROCEDURE FindInScope*(name: ARRAY OF CHAR; scope: Object; VAR res: Object);
  3. VAR
  4. obj, head: Object;
  5. inRec: BOOLEAN;
  6. BEGIN
  7. head := scope;
  8. inRec := FALSE;
  9. LOOP
  10. IF (head.link#NIL)&(head.link.mode=Typ) THEN (*inside a record*)
  11. FindField(name, head.link.typ, obj, TRUE);
  12. IF inRec & (obj # NIL) & ~(obj.mode IN {Typ, Con}) THEN err(200) END; (*a record inside a record, cannot access its fields/methods*)
  13. inRec := TRUE;
  14. IF (obj # NIL) OR head.link.typ.incomplete THEN EXIT END ;
  15. ELSE
  16. obj := head^.right;
  17. LOOP
  18. IF obj = NIL THEN EXIT END ;
  19. IF name < obj^.name THEN
  20. obj := obj^.left
  21. ELSIF name > obj^.name THEN
  22. obj := obj^.right
  23. ELSE (*found*)
  24. IF inRec & (head.link # NIL) & (head.link.mode # Mod) & ~(obj.mode IN {Typ, Con}) THEN err(200) END; (*record accesses proc vars: illegal*)
  25. INCL(obj.flag, used);
  26. EXIT
  27. END
  28. END ;
  29. IF obj # NIL THEN EXIT END;
  30. END;
  31. head := head^.left;
  32. IF head = NIL THEN EXIT END
  33. END ;
  34. IF (obj # NIL) & (obj.mode < 0) THEN obj := NIL END;
  35. res := obj;
  36. END FindInScope;
  37.  

Как говориться... Естественно вставка объекта так же громоздка... Поэтому код и понимание усложняются.

На второй вопрос ответ простой, есть некоторые, не очень популярные в свете операционные системы (вроде MS Windows) в которых данные и параметры создаются добавлением в начало списка (как у нас со стандартными типами), поэтому, для генерации кода приходиться рекурсивно перемещать код (менять его местами), а значит прямая кодогенерация хоть и возможна, но немного сложна и запутана (хотя... менять местами части массива практически тоже самое, просто заставляет вводить дополнительные инструменты для выделения памяти хранения временных кусков)

Ну засим всё... В следущем разе :) перейдём к ядру и самому важному (но не сложному) разбору выражений и синтаксическому сахару!

PS...
Ещё пару слов о FPC.
Функции:
Код: "OBERON"
  1.  
  2. PROCEDURE NEW(VAR x: Type_);
  3. PROCEDURE NEW(VAR x: Item); OVERLOAD;
  4. PROCEDURE NEW(VAR x: Object_); OVERLOAD;

Заменяют паскалевскую конструкцию x := *.Create; и добавляют созданный объект в сборщик мусора GC.Add(x);
То есть прямо эмулируют внутренний оператор NEW Оберона.


Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 28 ноя 2018, 07:36 
Не в сети

Сообщения: 203
А почему был начат ещё один новый и скорее всего никому не нужный проект компилятора, вместо того, что бы допиливать какой-то из существующих? Например, компилятор от akron выглядит самым реалистичным из существующих компиляторов Оберона-07.


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

Сообщения: 189
geniepro писал(а):
А почему был начат ещё один новый и скорее всего никому не нужный проект компилятора

Это будет даже не проект, а скорее визуальное наглядное пособие как работает компилятор и почему...
Очень много в сети кода с исходниками, и очень мало объяснений почему. Вот в этом плане, конечно, уважаемые в свете Saferoll и Олежек сделали очень много, но понять почему это так работает можно лишь после того, как поймёшь как всё происходит в коде.


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

Сообщения: 189
Продолжим начатое.
Следующий шаг в нашем проекте - создание функций для разбора выражений. Я считаю, что это и есть ядро любого компилятора, причем самое важное. Ведь именно здесь решаются такие вопросы как:

1) Будут ли допустимы разнотипные выражения или будут сделаны приведения типов автоматически?
Например выражение 1+4/3-3 имеет после вычисления тип REAL но включает в себя тип INTEGER, что не запрещается допустим в BB (BlackBox) и не допускается в Обероне 07. Тоесть строгость кодирования в 07 выше.

Изображение

2) Первичные оптимизации.
3) В основном на этом этапе и принимаются решения о расширениях языка (унарные операции,константные массивы и т.д)

Вобщем от слов к делу. Создаём новый модуль MCE.pas (Micro Oberon Expression)
Четыре функции отвечающие за разбор выражения:
Код: "OBERON"
  1.  
  2. PROCEDURE Factor(VAR x: MCB.Item);
  3. PROCEDURE Term(VAR x: MCB.Item);
  4. PROCEDURE SimpleExpression(VAR x: MCB.Item);
  5. PROCEDURE Expression(VAR x: MCB.Item);
  6.  

И одна функция для проверки ошибок, симплификации(упрощения) нашего дерева, ну и вообще отвечающую за строгость реализации:
Код: "OBERON"
  1.  
  2. PROCEDURE CheckTypes(op: INTEGER; VAR x, y: MCB.Item);
  3.  

Именно сдесь мы и будем проверять соответствие типов, считать и создавать новые узлы дерева.

Я внимательно почитал изучение вопроса об унарном минусе и поэтому модифицировал MCE.Factor с учетом приоритетности унарного минуса!

Во вложении как всегда пример, пример - визуальный, дерево выводится прямо на форму.
Так как не очень силён в построении графов, то уж вывод сделал как мог, не судите строго!

В следущем разе рассмотрим, как раз, всевозможные аспекты разбора выражений :)

Изображение


Вложения:
part2.zip [878.55 КБ]
Скачиваний: 334
Вернуться к началу
 Профиль  
Ответить с цитатой  
СообщениеДобавлено: 28 ноя 2018, 23:02 
Не в сети

Сообщения: 350
Сергей, спасибо, уже взял кое-что из ваших разъяснений. А SovietPony зажал свои записки...


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

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


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

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


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

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