Вверх ↑
Этот топик читают: Гость
Администрация
Ответов: 15295
Рейтинг: 1519
#1: 2011-06-03 13:23:11 ЛС | профиль | цитата
Аннотация
   Поскольку RTCG в перспективе должен стать основным инструментом, используемым в разработки пакетов, то имеет смысл написать небольшой цикл статей-лекций, вводящий в курс дела в первую очередь тех, кто еще не знаком с кодогенерацией данного типа, и тех, кто с ней знаком на примере FTCG. Основное внимание будет уделено не синтаксису скрипта (который можно узнать, потратив 5 минут на чтение спецификации), а его архитектуре и взаимодействию со средой и ее компонентами.

   Примеры, приводимые по ходу подачи информации в основном будут относиться к пакету Lazarus, но могут быть перенесены на другие пакеты с точностью до целевого языка.

Общая архитектура
   Ниже представлена схема основных блоков кодогенератора RTCG и ее сравнение с кодогенератором прошлого поколения:

Архитектура кодогенераторов FTCG и RTCG

Пояснение к блокам:
- Точка входа - так называется элемент на схеме, с которого начинается любая кодогенерация в пакетах на базе *TCG. Название элемента может быть любым, но его скрипт обязан содержать как минимум ф-цию doStart, на которую и передается управление после начала процедуры сборки проекта
- Обход элементов - это процедура последовательного вызова функций в скриптах элементов в соответствии с их подключением на схеме. Поскольку процесс рекурсивный (т.е. каждый элемент вызывает метод другого элемента самостоятельно), то их "лобовое" кольцевание на схеме в кодогенераторах *TCG исключено и приводит к ошибке
- Загрузка скрипта - загрузка текста скрипта с диска в оперативную память
- Парсинг скрипта - разбор синтаксиса скрипта и формирование байт кода для его дальнейшего исполнения
- Выполнение скрипта - в FTCG выполнение скрипта совмещено с парсингом и выполняется для каждого метода каждого элемента при каждом вызове, в RTCG выполняется байт код, полученный на предыдущем шаге
- Контекст элемента - под этим термином понимается наличие некоторого набора переменных, значения которых зависят от того, для какого элемента в данный момент выполняется скрипт
- Обход свободных элементов - блок аналогичен блоку Обход элементов с той лишь разницей, что кодогенератор самостоятельно в цикле обходит все элементы схемы, никак не связанные с другими элементами и вызывает для каждого из них метод с названием init
- Создание кода из блока Result - завершающая операция, которая получает содержимое блока Result ввиде текста и отправляет его в качестве результата кодогенерации в среду.

Отличия в архитектурах FTCG и RTCG
   Как можно заметить по схеме отличий не так уж и много:
- пользовательские расширения кодогенератора заменены на системный юнит с именем Sys, который парсится один раз перед вызовом метода doStart
- этапы парсинга и выполнения кода элемента разнесены в отдельные блоки, причем парсинг выполняется только один раз при первом обращении к элементу
- контекст элемента в FTCG автоматически выставлялся только при вызове метода другого элемента оператором event, теперь же за правильностью контекста следит сам кодогенератор и никакого ручного вмешательства не требуется

Системный модуль Sys
   Как уже было отмечено выше это модуль, который призван заменить файл direct.inc, встраиваемый непосредственно в кодогенератор и компилируемый вместе с ним. Основное его назначение - реализовать функционал, который будет описывать некоторые особенности целевого языка, необходимые кодогенератору для правильного функционирования.


#hws
// вызывается один раз при загрузки модуля
func create(entry)
// настройка кодогенератора
// создание блоков
// регистрация типов и т.д.
end

// вызывается один раз при выгрузки модуля
func destroy(entry)
// уничтожение блоков
end

// вызывается всякий раз при необходимости привести выражение value к типу type
func to_type(value, type)
end

// пользовательский не обязательный метод
func my_proc()
// что-то с чем-то делаем
end

   Кроме того в данном модуле можно разместить любое количество пользовательских методов, доступ к которым есть в любом скрипте через объект sys:

#hws
sys.my_proc()

Типичная структура пакета
   В простейшем случае весь цикл процессов от нажатия пользователем кнопки Build в среде, до получения кода целевого проекта можно уложить в несколько простых шагов:
- вызов метода sys.create()
- вызов метода EntryPoint.doStart() (где EntryPoint это основной, родительский, элемент текущего проекта)
- последовательный вызов методов всех соединенных друг с другом элементов (если они есть)
- вызов метода sys.destroy()

   При этом результат работы кодогенератор ожидает в блоке с именем "result" - как, чем и какими судьбами формируется код в этом блоке - ему совершенно не важно. Поэтому в самом простом случае полностью рабочий пакет на RTCG можно сделать только из одного элемента с одной строкой кода:

#hws
block.reg("result").println('void main() { }')

   Очевидно, что для полноценного пакета этого не достаточно. Даже при наличии совсем простого целевого языка весь будущий код приложения необходимо собрать по кусочкам, слепленным из разных элементов схемы. Т.е. встает задача формирования отдельных сегментов, которые собираются в конечное цельное приложение только в самом конце всего процесса. Именно на этом принципе и построены все пакеты *TCG - в самом начале создается множество блоков, отвечающих за накопление переменных, объявление и реализацию методов, за код инициализации и код деинициализации и т.д. и т.п. и только в конце формируется тот код, который уже пригоден для непосредственной компиляции.
   Рассмотрим эту архитектуру подробнее на примере пакета Lazarus с целевым языком Pascal. Пакет построен таким образом, что любой уровень вложенности схемы (в том числе и самый первый, в котором расположен основной элемент проекта) представляет из себя unit с одним классом. Например, код пустой схемы с одной формой имеет такую структуру:

#pas
// --- начало блока 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
   И того в самом простом случае для сборки одного юнита будущей программы (блок BODY) необходимо задействовать дополнительно 6 блоков:
- UNITS используемые системные модули
- PRIVATE VARS переменные, доступные любому элементу схемы в рамках текущего уровня вложенности
- METHODS INT заголовки методов
- METHODS IMP реализации методов
- LOCAL VARS локальные переменные, доступные только одному конкретному элементу
- INIT секция инициализации

   С точки зрения кодогенератора весь процесс происходит следующим образом:
- создается 6 описанных выше блоков
- вызывается метод onStart, в котором по цепочке выполняются методы всех элементов схемы
- элементы добавляют в блоки нужные для их работы куски кода
- все 6 блоков склеиваются в один блок BODY в соответствии с шаблоном выше
- блок BODY сохраняется на диск ввиде готового unit-а
- все блоки удаляются

   Посмотрим, как будет выглядеть код unit-а главной формы, если в схему добавить кнопку Push, по нажатию которой в Label-е появляется текст "Hello world":


#pas
// --- начало блока 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

   Хорошо видно, что новые элементы на схеме задействовали несколько блоков, куда разместили код, обеспечивающий их корректную работу в соответствии с заявленными функциями.
карма: 27
4
файлы: 1rtcg-chart.png [71.2KB] [952]
Голосовали:iarspider, login, CriDos, brown-aleks
Разработчик
Ответов: 26113
Рейтинг: 2126
#2: 2011-06-03 13:45:50 ЛС | профиль | цитата
Dilma писал(а):
который можно узнать, потратив 5 минут на чтение спецификации

Ну, сказочник За 5-ть минут выучить синтаксис, это ж каким гением надо быть
А если серьезно, то статья полезная. Любая дополнительная информация полезна. Вот еще бы окончательный вариант последнего кода, заключенный в оболочку скрипта привел бы, цены бы ему не было, что бы наглядно видеть, как все это дело выглядит в скрипте RTCG
------------ Дoбавленo в 13.45:
Да, кстати, а как описывается секция private, ты описал секцию public, назвав ее PRIVATE. Или секции private не будет, как таковой
карма: 22

0
Администрация
Ответов: 15295
Рейтинг: 1519
#3: 2011-06-03 13:47:30 ЛС | профиль | цитата
nesco писал(а):
За 5-ть минут выучить синтаксис, это ж каким гением надо быть

nesco, после знакомства с 1-2мя языками все последующие не учатся, а исследуются на предмет аналогий (назовем это так). Если ты знаешь, что в Pascal условный блок записывается как:

#pas
if condition then
// true statement
else
// false statement

то ты чего заново будешь изучать аналогичную конструкцию RCTG

#hws
if(condition)
// true statement
else
// false statement
end

извини, не получается в это поверить.

nesco писал(а):
Вот еще бы окончательный вариант последнего кода, заключенный в оболочку скрипта

подача информации порциальная, перегружать статьи не стоит
карма: 27
0
Разработчик
Ответов: 4698
Рейтинг: 426
#4: 2011-06-03 13:50:40 ЛС | профиль | цитата
Dilma, а почему бы сразу не в блог? В который потом можно будет дописывать инфу.
------------ Дoбавленo в 13.50:
Dilma, а исходники, полученные кодогенератором пакета Lazarus, будут компилиться в режиме совместимости с Delphi? ИМХО, это было бы не очень хорошо.
карма: 10
0
Администрация
Ответов: 15295
Рейтинг: 1519
#5: 2011-06-03 13:50:58 ЛС | профиль | цитата
nesco писал(а):
Да, кстати, а как описывается секция private, ты описал секцию public, назвав ее PRIVATE. Или секции private не будет, как таковой

подробнее о блоках будет в другом разделе, пока же предлагаю просто не связывать названия блоков с названиями секций целевого языка, т.к. описанная архитектура характерна практически для всех пакетов на базе *TCG и никак не связана с конструкциями Pascal или любого иного языка
карма: 27
0
Разработчик
Ответов: 26113
Рейтинг: 2126
#6: 2011-06-03 14:11:19 ЛС | профиль | цитата
Dilma писал(а):
то ты чего заново будешь изучать аналогичную конструкцию RCTG

Ну ты и привел пример для сравнения -- условные операторы во многих ЯВУ похожи (ага, что не скажешь про case, который тут switch). Я же говорил про специфику синтаксиса, присущего конкретной адаптированной скриптовой оболочке. А ведь в ней есть специфические операторы
карма: 22

0
Разработчик
Ответов: 4698
Рейтинг: 426
#7: 2011-06-03 14:12:54 ЛС | профиль | цитата
nesco писал(а):
А ведь в ней есть специфические операторы

Которые так же имеют аналоги в других ЯП, только в виде реализуемых функцийобъектов
карма: 10
0
Разработчик
Ответов: 26113
Рейтинг: 2126
#8: 2011-06-03 14:14:06 ЛС | профиль | цитата
Assasin писал(а):
Которые так же имеют аналоги в других ЯП, только в виде реализуемых функцийобъектов

Согласись, что за 5-ть минут это не изучишь
карма: 22

0
Разработчик
Ответов: 4698
Рейтинг: 426
#9: 2011-06-03 14:16:26 ЛС | профиль | цитата
nesco писал(а):
Согласись, что за 5-ть минут это не изучишь

Ну за 5 минут действительно многого не изучишь, пожалуй, тут Dilma употребил преувеличение. Но за день вполне можно полностью изучить все особенности такого простого языка кодогенерации (хотя есть все-таки исключения: язык BrainFuck Он учится действительно за 5 минут )
карма: 10
0
Разработчик
Ответов: 26113
Рейтинг: 2126
#10: 2011-06-03 14:24:33 ЛС | профиль | цитата
Почитав, я понял, что существует некий препроцессинг кода скрипта, который запускается при чтении элементов схемы. Окончательный же код целевого языка собирается уже на этапе генерации. И очень похоже на то, что препроцессинг никак не зависит от целевого языка и унифицирован для всех пакетов
карма: 22

0
Ответов: 4629
Рейтинг: 749
#11: 2011-06-03 14:28:38 ЛС | профиль | цитата
nesco, что ты переживаешь? Поверь, ничего сложного там нет. Можешь заглянуть в штатную справку по FTCG. Элементов языка там - действительно на 5 минут (запомнить, какие команды есть, а затем по мере написания кода изучать их подробнее). Чуть больше времени понадобится, чтобы изучить принципы строения кода компонентов и набить на этом руку. А вот что может занять много времени, так это изучение новой библиотеки LCL.
nesco писал(а):
существует некий препроцессинг кода скрипта

Ты правильно понял. Для большей ясности может это почитай:
http://www.hiasm.com/forum.html?q=3&t=54530
карма: 26

0
Разработчик
Ответов: 26113
Рейтинг: 2126
#12: 2011-06-03 14:53:12 ЛС | профиль | цитата
Netspirit писал(а):
А вот что может занять много времени, так это изучение новой библиотеки LCL

Ну, насколько я понл, там народ не мудрствовал лукаво и сдул функционал VCL, со всем выходящим
карма: 22

0
Администрация
Ответов: 15295
Рейтинг: 1519
#13: 2011-06-03 15:27:21 ЛС | профиль | цитата
Netspirit писал(а):
А вот что может занять много времени, так это изучение новой библиотеки LCL.

на мой взгляд количество времени, затрачиваемое на изучение материала для написания пакета примерно такое:
1) синтаксис RTCG - 5-10 минут
2) архитектура пакета на базе RTCG - 40-60 минут
3) библиотеки RTL, FCL и LCL - 48 часов-бесконечность

первое решается спецификацией, второе статьями, которые желательно написать, третье - офф. документацией по FPC и Lazarus
------------ Дoбавленo в 15.27:
nesco писал(а):
Почитав, я понял, что существует некий препроцессинг кода скрипта, который запускается при чтении элементов схемы. Окончательный же код целевого языка собирается уже на этапе генерации

не следует вводить такое количество новых определений ( "препроцессинг", "чтении элементов", "этапе генерации") - это ясности не внесет, а наделает только еще больше путаницы, потребующей дополнительных разъяснений
карма: 27
0
Ответов: 1821
Рейтинг: 168
#14: 2011-11-13 00:09:22 ЛС | профиль | цитата
[offtop]Не по теме, НО - как переводится RTCG и FTCG[/offtop]
карма: 5

0
Ответов: 1731
Рейтинг: 68
#15: 2011-11-13 00:35:05 ЛС | профиль | цитата
[offtop]
Справка писал(а):

FTCG - Flow Threading Code Generation - потоковая генерация кода. Осной принцип, на базе которого построена генерация оптимального кода в пакете WEB, QT и прочих.


А RTCG не знаю.[/offtop]
карма: 1

0
Сообщение
...
Прикрепленные файлы
(файлы не залиты)