| RTCG | - RTCG - построение архитектуры пакета |
RTCG - построение архитектуры пакета
Введение
Написание своего пакета необходимо начинать с реализации файла hiSys.hws - именно в этом файле определяются все общие инструменты будущего пакета, его блоки, глобальные переменные, типы данных и прочее. Обязательное минимальное наполнение этого модуля следующее:
func create(entry)
// тут необходимо объявить все глобальные переменные проекта, используемые блоки, типы и прочее
end
func destroy(entry)
// тут необходимо удалить все используемые блоки, файлы и прочее
end
func to_type(value, type)
// тет необходимо реализовать конвертацию значения value в тип type и вернуть его в качестве результата
end
Типичная структура пакета
В простейшем случае весь цикл процессов от нажатия пользователем кнопки Build в среде, до получения кода целевого проекта можно уложить в несколько простых шагов:
- вызов метода sys.create()
- вызов метода EntryPoint.doStart() (где EntryPoint это основной, родительский, элемент текущего проекта)
- последовательный вызов методов всех соединенных друг с другом элементов (если они есть)
- вызов метода sys.destroy()
При этом результат работы кодогенератор ожидает в блоке с именем "result" - как, чем и какими судьбами формируется код в этом блоке - ему совершенно не важно. Поэтому в самом простом случае полностью рабочий пакет на RTCG можно сделать только из одного элемента с одной строкой кода:
Очевидно, что для полноценного пакета этого не достаточно. Даже при наличии совсем простого целевого языка весь будущий код приложения необходимо собрать по кусочкам, слепленным из разных элементов схемы. Т.е. встает задача формирования отдельных сегментов, которые собираются в конечное цельное приложение только в самом конце всего процесса. Именно на этом принципе и построены все пакеты *TCG - в самом начале создается множество блоков, отвечающих за накопление переменных, объявление и реализацию методов, за код инициализации и код деинициализации и т.д. и т.п. и только в конце формируется тот код, который уже пригоден для непосредственной компиляции.
Рассмотрим эту архитектуру подробнее на примере пакета Lazarus с целевым языком Pascal. Пакет построен таким образом, что любой уровень вложенности схемы (в том числе и самый первый, в котором расположен основной элемент проекта) представляет из себя unit с одним классом. Например, код пустой схемы с одной формой имеет такую структуру:
И того в самом простом случае для сборки одного юнита будущей программы (блок BODY) необходимо задействовать дополнительно 6 блоков:
- UNITS используемые системные модули
- PRIVATE VARS переменные, доступные любому элементу схемы в рамках текущего уровня вложенности
- METHODS INT заголовки методов
- METHODS IMP реализации методов
- LOCAL VARS локальные переменные, доступные только одному конкретному элементу
- INIT секция инициализации
С точки зрения кодогенератора весь процесс происходит следующим образом:
- создается 6 описанных выше блоков
- вызывается метод onStart, в котором по цепочке выполняются методы всех элементов схемы
- элементы добавляют в блоки нужные для их работы куски кода
- все 6 блоков склеиваются в один блок BODY в соответствии с шаблоном выше
- блок BODY сохраняется на диск ввиде готового unit-а
- все блоки удаляются
Посмотрим, как будет выглядеть код unit-а главной формы, если в схему добавить кнопку Push, по нажатию которой в Label-е появляется текст "Hello world":
Хорошо видно, что новые элементы на схеме задействовали несколько блоков, куда разместили код, обеспечивающий их корректную работу в соответствии с заявленными функциями.
- вызов метода sys.create()
- вызов метода EntryPoint.doStart() (где EntryPoint это основной, родительский, элемент текущего проекта)
- последовательный вызов методов всех соединенных друг с другом элементов (если они есть)
- вызов метода sys.destroy()
При этом результат работы кодогенератор ожидает в блоке с именем "result" - как, чем и какими судьбами формируется код в этом блоке - ему совершенно не важно. Поэтому в самом простом случае полностью рабочий пакет на RTCG можно сделать только из одного элемента с одной строкой кода:
func create(entry)
block.reg("result").println('void main() { }')
end
Очевидно, что для полноценного пакета этого не достаточно. Даже при наличии совсем простого целевого языка весь будущий код приложения необходимо собрать по кусочкам, слепленным из разных элементов схемы. Т.е. встает задача формирования отдельных сегментов, которые собираются в конечное цельное приложение только в самом конце всего процесса. Именно на этом принципе и построены все пакеты *TCG - в самом начале создается множество блоков, отвечающих за накопление переменных, объявление и реализацию методов, за код инициализации и код деинициализации и т.д. и т.п. и только в конце формируется тот код, который уже пригоден для непосредственной компиляции.
Рассмотрим эту архитектуру подробнее на примере пакета Lazarus с целевым языком Pascal. Пакет построен таким образом, что любой уровень вложенности схемы (в том числе и самый первый, в котором расположен основной элемент проекта) представляет из себя unit с одним классом. Например, код пустой схемы с одной формой имеет такую структуру:
// --- начало блока BODY
Unit hiMainForm1;
interface
// --- начало блока UNITS
uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, StdCtrls;
// --- конец блока UNITS
type
TMainForm1 = class(TForm)
protected
procedure DoCreate; override;
procedure addWidget(widget:TControl; x,y,w,h:integer);
public
// --- начало блока PRIVATE VARS
{ для пустого проекта эта секция останется пустой }
// --- конец блока PRIVATE VARS
// --- начало блока METHODS INT
{ для пустого проекта эта секция останется пустой }
// --- конец блока METHODS INT
end;
implementation
procedure TMainForm1.addWidget(widget:TControl; x,y,w,h:integer);
begin
widget.parent := self;
widget.left := x;
widget.top := y;
widget.width := w;
widget.height := h;
end;
procedure TMainForm1.DoCreate;
// --- начало блока LOCAL VARS
{ для пустого проекта эта секция останется пустой }
// --- конец блока LOCAL VARS
begin
Caption := 'Form1';
// --- начало блока INIT
{ для пустого проекта эта секция останется пустой }
// --- конец блока INIT
end;
// --- начало блока METHODS IMP
{ для пустого проекта эта секция останется пустой }
// --- конец блока METHODS IMP
end.
// --- конец блока BODY
- UNITS используемые системные модули
- PRIVATE VARS переменные, доступные любому элементу схемы в рамках текущего уровня вложенности
- METHODS INT заголовки методов
- METHODS IMP реализации методов
- LOCAL VARS локальные переменные, доступные только одному конкретному элементу
- INIT секция инициализации
С точки зрения кодогенератора весь процесс происходит следующим образом:
- создается 6 описанных выше блоков
- вызывается метод onStart, в котором по цепочке выполняются методы всех элементов схемы
- элементы добавляют в блоки нужные для их работы куски кода
- все 6 блоков склеиваются в один блок BODY в соответствии с шаблоном выше
- блок BODY сохраняется на диск ввиде готового unit-а
- все блоки удаляются
Посмотрим, как будет выглядеть код unit-а главной формы, если в схему добавить кнопку Push, по нажатию которой в Label-е появляется текст "Hello world":
// --- начало блока BODY
Unit hiMainForm1;
interface
// --- начало блока UNITS
uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, StdCtrls;
// --- конец блока UNITS
type
TMainForm1 = class(TForm)
protected
procedure DoCreate; override;
procedure addWidget(widget:TControl; x,y,w,h:integer);
public
// --- начало блока PRIVATE VARS
Button2:TButton;
Label3:TLabel;
// --- конец блока PRIVATE VARS
// --- начало блока METHODS INT
procedure onClick2(Sender:TObject);
// --- конец блока METHODS INT
end;
implementation
procedure TMainForm1.addWidget(widget:TControl; x,y,w,h:integer);
begin
widget.parent := self;
widget.left := x;
widget.top := y;
widget.width := w;
widget.height := h;
end;
procedure TMainForm1.DoCreate;
// --- начало блока LOCAL VARS
{ эта секция останется пустой }
// --- конец блока LOCAL VARS
begin
Caption := 'Form1';
// --- начало блока INIT
Button2 := TButton.Create(self);
addWidget(Button2, 35, 20, 55, 30);
Button2.Caption := 'Push';
Button2.onClick := @onClick2;
Label3 := TLabel.Create(self);
addWidget(Label3, 60, 30, 40, 30);
Label3.Caption := 'Label';
// --- конец блока INIT
end;
// --- начало блока METHODS IMP
procedure TMainForm1.onClick2(Sender:TObject);
begin
Label3.Caption := 'hello world';
end;
// --- конец блока METHODS IMP
end.
// --- конец блока BODY
Хорошо видно, что новые элементы на схеме задействовали несколько блоков, куда разместили код, обеспечивающий их корректную работу в соответствии с заявленными функциями.
BB-code статьи для вставки
Всего комментариев: 0
(комментарии к статье еще не добавлены)