Предыдущая серия была тут - Собираем всё в кучу. И наследование и реализация и множественное наследование.
Я немного порефакторил и получилось вот что:
https://bitbucket.org/lulinalex/mindstream/src/a71b753edfec2d6f03568518d328cf20b59a8d1b/Examples/Scripts/CodeGeneration/CodeGen55.ms.script?at=B284
Я немного порефакторил и получилось вот что:
https://bitbucket.org/lulinalex/mindstream/src/a71b753edfec2d6f03568518d328cf20b59a8d1b/Examples/Scripts/CodeGeneration/CodeGen55.ms.script?at=B284
USES metaMACRO.ms.dict classRelations.ms.dict ; Test CodeGen %REMARK ' CodeGen - это функция в которой мы будем тестировать наш функционал ' %REMARK ' %SUMMARY это мета-информация, которая позволяет привязывать документацию к элементам кода. Эта документация доступна потом из скриптовой машины. ' %SUMMARY ' Тут будем тестировать построение сначала мета-модели, потом модели, а потом и кодогенерации ' ; // %SUMMARY // --------------------------------------------------------------------------- meta-meta-model-begin 'Тут будем определять аксиоматику мета-мета-модели, а потом вынесем её в отдельный словарь. ' StereotypeStereotypeProducer meta %SUMMARY ' Определяем базовый элемент мета-модели Тот самый который позволяет тащить всё остальное "за волосы из болота" Через этот примитив выводятся все остальные ' ; // %SUMMARY ; // meta meta-meta-model-end // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- meta-model-begin 'Тут будем определять аксиоматику мета-модели, а потом вынесем её в отдельный словарь. Дальше будем определять понятия из UML - https://ru.wikipedia.org/wiki/UML Там бывают КАТЕГОРИИ и КЛАССЫ (Category и Class) На самом деле разница между ними - "призрачна", но раз умные дяди так решили, то так тому и быть Вот с них и начнём: ' <<@meta>> UMLCategory %SUMMARY ' Категория в терминах UML ' ; // %SUMMARY ; // UMLCategory <<@meta>> UMLClass %SUMMARY ' Класс в терминах UML ' ; // %SUMMARY ; // UMLClass meta-model-end // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- concrete-model-begin 'Модель шаблонов' 'Тут будем определять аксиоматику конкретной модели. Пока - "модели шаблонов". А потом вынесем её в отдельный словарь. ' <<UMLCategory>> Project %SUMMARY ' Наверное у нас при разработке встречаются проекты. Так вот Project это стереотип, который описывает наши проекты. ' ; // %SUMMARY ; // Project <<UMLCategory>> Library %SUMMARY ' Наверное у нас при разработке встречаются проектные библиотеки. Так вот Library это стереотип, который описывает наши библиотеки. ' ; // %SUMMARY ; // Library <<UMLCategory>> Programm %SUMMARY ' Наверное у нас при разработке встречаются программы. Так вот Programm это стереотип, который описывает наши программы. ' ; // %SUMMARY ; // Programm <<UMLClass>> Class %SUMMARY ' Наверное у нас при разработке встречаются проектные классы. Так вот Class это стереотип, который описывает наши проектные классы. ' ; // %SUMMARY ; // Class <<UMLClass>> Interface %SUMMARY ' Наверное у нас при разработке встречаются интерфейсы. Так вот Interface это стереотип, который описывает наши интерфейсы. ' ; // %SUMMARY ; // Interface %REMARK ' Могут ли Library вкладываться в Project, а Project в Library Или могут ли Programm вкладываться в Class, а Class в Programm И прочие отношения между стереотипами - мы определим несколько позже. Когда начнём использовать их. ' model-end // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- concrete-model-begin 'Модель конкретного проекта Project1' 'Тут будем определять аксиоматику конкретной модели конкретного проекта. А потом вынесем её в отдельный словарь. ' <<Project>> Project1 %SUMMARY ' Это наш первый проект - Project1 ' ; // %SUMMARY <<Library>> Library1 %SUMMARY ' Наверное наш проект содержит какие-то проектные библиотеки. Так вот Library1 - это наша первая проектная библиотека ' ; // %SUMMARY ; // Library1 <<Library>> Library2 %SUMMARY ' Наверное наш проект достаточно серьёзен и содержит НЕ ОДНУ библиотеку. Так вот Library2 - это наша вторая проектная библиотека. ' ; // %SUMMARY ; // Library2 <<Library>> Library3 %SUMMARY ' Наверное наш проект НАСТОЛЬКО серьёзен, что содержит даже НЕ ДВЕ библиотеки. Так вот Library3 - это наша третья проектная библиотека. ' ; // %SUMMARY ; // Library3 <<Programm>> Programm1 %SUMMARY ' Наверное наш проект реализует какую-то программу. Иначе - зачем бы он нам был бы нужен? Так вот Programm1 - это программа внутри нашего проекта Project1. ' ; // %SUMMARY <<Class>> Class1 %SUMMARY ' Наверное наша программа содержит какие-то классы реализации. Иначе - кто будет реализовывать наш функционал? Так вот Class1 - это наш ПЕРВЫЙ класс реализации внутри нашей программы Programm1. ' ; // %SUMMARY ; // Class1 <<Interface>> Interface1 %SUMMARY ' Наверное наша программа настолько серьёзна, что реализует какие-то интерфейсы. Так вот Interface1 - это наш ПЕРВЫЙ интерфейс. ' ; // %SUMMARY ; // Interface1 <<Interface>> Interface2 %SUMMARY ' Наверное наша программа настолько серьёзна, что реализует НЕ ОДИН интерфейс, а несколько. Так вот Interface2 - это наш ВТОРОЙ интерфейс. ' ; // %SUMMARY ; // Interface2 <<Class>> Class2 %SUMMARY ' Наверное наша программа достаточно серьёзна и содержит НЕ ОДИН классы реализации. Так вот Class2 - это наш ВТОРОЙ класс реализации внутри нашей программы Programm1. ' ; // %SUMMARY %INHERITS @ Class1 %REMARK 'Возможно наш проектный класс Class2 наследуется от класса Class1' ; // %INHERITS %IMPLEMENTS @ Interface1 %REMARK 'Возможно наш проектный класс Class2 реализует интерфейс Interface1' @ Interface2 %REMARK 'Возможно наш проектный класс Class2 реализует ещё и интерфейс Interface2' ; // %IMPLEMENTS ; // Class2 <<Class>> Class3 %SUMMARY ' Возможно, что у нас такая непростая программа, что в ней даже больше, чем ДВА класса реализации. Так вот Class3 - это наш ТРЕТИЙ класс реализации внутри нашей программы Programm1. ' ; // %SUMMARY ; // Class3 <<Class>> Class4 %SUMMARY ' Возможно, что мы настолько офигенно круты, что у на даже НЕ ТРИ класса реализации. Так вот Class4 - это наш ЧЕТВЁРТЫЙ класс реализации внутри нашей программы Programm1. ' ; // %SUMMARY %INHERITS @ Class2 @ Class3 %REMARK ' Возможно, что мы нстолько ОФИГЕННЫЕ перцы, что используем МНОЖЕСТВЕННОЕ наследование. И даже ПОНИМАЕМ - ЗАЧЕМ это нужно. Так вот Class4 - наследуется от Class2 и Class3. ' ; // %INHERITS ; // Class4 ; // Programm1 ; // Project1 %REMARK ' РЕМАРКА. Все эти слова "наверное" вообще говоря должны проистекать из требований, ТЗ и UseCase Но мы про это позже поговорим. ' model-end // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- concrete-model-begin 'Модель конкретного проекта Project2' 'Тут будем определять аксиоматику конкретной модели конкретного проекта. А потом вынесем её в отдельный словарь. ' <<Project>> Project2 %SUMMARY ' Это наш ВТОРОЙ проект - Project2 ' ; // %SUMMARY ; // Project2 model-end // --------------------------------------------------------------------------- USES CodeDump.ms.dict // - тут подключаем словарь CodeDump.ms.dict, чтобы "увидеть" слово DumpElement ; @SELF DumpElement %REMARK ' - тут дампим элемент CodeGen и его содержимое в стандартное устройство вывода. Чисто для отладки того, что мы сейчас написали. ' help %REMARK ' Выводим всю доступную аксиоматику в стандартное устройство вывода. Чисто для отладки того, что мы сейчас написали. ' %REMARK ' Теперь, что мы можем сделать с нашим проектом? Ну для начала выведем его содержимое на стандартное устройство вывода. ' TtfwWord TYPE ModelElement %REMARK 'Элемент модели' PROCEDURE do_elem_func STRING IN aName TtfwWord IN aSelf TtfwWord IN aModifier %SUMMARY 'Реализация do_elem_func, elem_proc и elem_generator' ; aSelf Ctx:SetWordProducerForCompiledClass axiom:PushSymbol : aName Ctx:Parser:PushLeftDottedSymbol axiom:PushSymbol ModelElement if ( aModifier <> nil ) then ( aModifier |N Ctx:Parser:PushSymbol ) axiom:PushSymbol in 'Self' Ctx:Parser:PushSymbol ; // do_elem_func MACRO elem_func Literal IN aName %SUMMARY 'Функция на элементе модели' ; aName |N @SELF nil do_elem_func ; // elem_func PROCEDURE do_elem_proc STRING IN aName TtfwWord IN aSelf TtfwWord IN aModifier %SUMMARY 'Реализация elem_proc и elem_generator' ; Ctx:ClearTypeInfo axiom:PushSymbol VOID aName aSelf aModifier do_elem_func ; // do_elem_proc MACRO elem_proc Literal IN aName %SUMMARY 'Процедура на элементе модели' ; aName |N @SELF nil do_elem_proc ; // elem_proc MACRO elem_generator Literal IN aName %SUMMARY 'Генератор содержимого элемента' ; aName |N @SELF nil do_elem_proc ; // elem_generator MACRO elem_ref_proc Literal IN aName %SUMMARY 'Процедура на элементе модели, который передаётся по ссылке' ; aName |N @SELF @ ^@ do_elem_proc ; // elem_ref_proc BOOLEAN elem_func IsSummary %SUMMARY ' Определяет тот факт, что aWord является документацией к элементу ' ; // %SUMMARY ( Self |N ) = '%SUM' >>> Result ; // IsSummary BOOLEAN elem_func IsModelElement %SUMMARY ' Определяет тот факт, что aWord является "элементом модели" ' ; // %SUMMARY NOT ( Self .IsSummary ) >>> Result ; // IsModelElement ARRAY elem_func Children %SUMMARY ' Возвращает итератор детей aWord в "терминах определённой модели" ' ; ( Self MembersIterator ) >filter> .IsModelElement >>> Result ; // Children INTEGER VAR g_Indent %REMARK 'Текущий отступ' g_Indent := 0 BOOLEAN elem_func IsElementNeedIndent %SUMMARY 'Определяет тот факт, что элементу нужен отступ' ; true >>> Result ; // IsElementNeedIndent elem_proc EnterElement %SUMMARY 'Начинает вывод элемента' ; Self .IsElementNeedIndent ? INC g_Indent ; // EnterElement elem_proc LeaveElement %SUMMARY 'Заканчивает вывод элемента' ; Self .IsElementNeedIndent ? DEC g_Indent ; // LeaveElement FILE VAR g_OutFile g_OutFile := nil STRING INTEGER ARRAY TYPE PRINTABLE PROCEDURE OutToFile PRINTABLE IN aValue %SUMMARY ' Выводит значение в текущий файл вывода. С переводом каретки. ' ; // %SUMMARY STRING VAR l_String if ( aValue IsArray ) then ( aValue strings:Cat >>> l_String ) else ( aValue ToPrintable >>> l_String ) [ g_Indent ' ' char:Dupe l_String ] strings:Cat g_OutFile File:WriteLn %REMARK '- выводим элементы модели в файл, а не в стандартный вывод.' ; //OutToFile FUNCTOR TYPE GENERATOR %REMARK 'Генератор содержимого элемента' elem_proc Child.CallGen GENERATOR right aGen %SUMMARY 'Вызывает на ДОЧЕРНЕМ элементе генератор aGen с учётом отступов' ; Self .EnterElement TRY Self aGen %REMARK 'Вызываем генератор aGen' FINALLY Self .LeaveElement END // TRY..FINALLY ; // Child.CallGen elem_generator DumpModelElement %SUMMARY ' Процедура печатающая содержимое элемента модели. Рекурсивно. ' ; // %SUMMARY [ Self |S %REMARK 'Выводим стереотип элемента' Self |N %REMARK 'Выводим имя элемента' ] ' ' strings:CatSep OutToFile TRY for ( Self .Children ) .Child.CallGen call.me %REMARK 'Выводим детей элемента' FINALLY [ '; // ' Self |N ] OutToFile %REMARK 'Выводим закрывающую скобку элемента' END ; // DumpModelElement STRING FUNCTION OutFileName STRING right aGeneratorName %SUMMARY 'Имя файла для вывода' ; script:FileName sysutils:ExtractFileName (+) '.' (+) aGeneratorName >>> Result ; // OutFileName elem_ref_proc CallGen GENERATOR right aGen %SUMMARY ' Вызывает на элементе генератор aGen. С открытием "правильных файлов". ' ; // %SUMMARY g_OutFile := ( OutFileName ( Self |N (+) '.' (+) 'dump' ) File:OpenWrite ) TRY Self aGen %REMARK 'Вызываем на элементе генератор aGen' FINALLY g_OutFile := nil END // TRY..FINALLY ; // CallGen Project1 .CallGen .DumpModelElement %REMARK 'Выводим содержимое элемента Project1' Project2 .CallGen .DumpModelElement %REMARK 'Выводим содержимое элемента Project2' ; // CodeGen CodeGen
Комментариев нет:
Отправить комментарий