Всем привет!
Начинаю разработку тестового компилятора совместимого с Оберон 07 но расширенного до определённых размеров!
Постараюсь учесть все нюансы (FOR, унарный минус, аспекты константных массивов и т.д.)
Для начала, как инструмент разработки, возьму FreePascal последней версии и максимально настрою его на работу в стиле Оберон...
Во вложении заготовка для экспериментов, опишу некоторые файлы.
Файл
RTL.mod - это эмулятор системных функций Оберонов
Код: "OBERON"
FUNCTION _DIV(CONST x, y: INT64): INT64;
FUNCTION _MOD(CONST x, y: INT64): INT64;
FUNCTION _ASH (e, bit : INTEGER) : INTEGER;
FUNCTION _LSL(CONST x, y: INTEGER): INTEGER;
FUNCTION _ASR(CONST x, y: INTEGER): INTEGER;
FUNCTION _ROR(CONST x, y: INTEGER): INTEGER;
FUNCTION _FLT(CONST i: INTEGER) : SINGLE;
FUNCTION _ITR(CONST i: INTEGER) : SINGLE;
FUNCTION _RTI(CONST r: SINGLE) : INTEGER;
FUNCTION _HTR(CONST i: INT64) : DOUBLE;
FUNCTION _RTH(CONST r: DOUBLE) : INT64;
FUNCTION _AND(CONST x, y: INTEGER): INTEGER;
FUNCTION _HASH(p: POINTER; l: INTEGER): INTEGER;
По моему тут все понятно и в коментариях не нуждается, однако оговорюсь, для пользователей, которые ещё пока не достаточно профессионально работают в Оберонах:
- Это реализация внутренних функций и операторов компилятора, так что можно посмотреть как работает та или иная директива.
Файл
MCS.mod - это лексический анализатор, его пока не будем изучать, оставим на потом...
Далее файл
MCB.mod - это уже файл компилятора с начальными настройками.
Именно он инициализирует настройки компилятора, создаёт базовые файлы и т.д. А так же включает в себя процедуры работы с деревом разбора синтаксических конструкций.
Вот на нём и остановимся подробно!
Как известно, Н.В. Вирт в своих компиляторах идёт двумя путями
1) Создаётся код, на прямую пишущийся в файл (большинство)
2) Создаётся дерево синтаксическое, а затем, если что то надо изменить, переместить и т.д. оно модифицируется и транслируется.
Вот вторым путём мы и пойдём, так как он немного гибче, поэтому заведём 3 структуры
Код: "OBERON"
Type_ = CLASS
...
END;
Item = CLASS
...
END;
Object_ = CLASS (Item)
...
END;
Как видно, Item реперь не RECORD (запись) а POINTER т.е. указатель, в связи с этим изменится и поведение нашего компилятора!
Две функции:
Код: "OBERON"
PROCEDURE Enter(name: ARRAY OF CHAR; clas: INTEGER; typ: Type_; val: LONGINT);
PROCEDURE Create(VAR x: Type_; form: INTEGER; size: LONGINT);
Отвечают за инициализацию базовых типов и привязывают объекты именованые к ним, НО!!!!! посмотрите как они добавлябтся в дерево, не в конец списка, а в начало
Код: "OBERON"
PROCEDURE Enter(name: ARRAY OF CHAR; clas: INTEGER; typ: Type_; val: LONGINT);
VAR
x: Object_;
BEGIN
...
x.c := hidden;
hidden := x;
...
END;
И наконец 3 функции отвечающие за видимость объектов (это же важно... что бы вложенные структуры видели все предыдущие объекты, но были недоступны на предыдущих уровнях. Так вот, функции:
Код: "OBERON"
PROCEDURE OpenScope;
PROCEDURE CloseScope;
PROCEDURE NewObj(VAR x: Object_; id: Ident; clas: INTEGER);
как раз за это и отвечают...
Процедуры OpenScope и CloseScope отвечают за переключение уровней видимости
Код: "OBERON"
x.dsc := topScope;
x.c := NIL;
topScope := x;
...
topScope := topScope.dsc
Для каждого объекта может быть создано поле .dsc в который и попадут внутренние объекты, например параметры процедур, локальные переменные, типы и константы
Во вложении маленькая программка которая и отображает выше-описаное, более того, это заготовка для экспортирования наших данных в файл экспорта!!!
Пример как она работает чисто визуальный:
Код: "OBERON"
INTEGER
BOOLEAN
O0
O1
O2
O3
O4
O5
O6
O7
O8
Видим, что глобальная видимость у певых 4-х веточек дерева, и на этом уровне не будут видны следующие...
Ну вот пока всё, в следующий раз подробно остановимся на константах, и нисходящем разборе выражений!