Прислали ключ к Delphi XE 10.
Да. Да - ДЕСЯТЬ. Если я ВСЁ ПРАВИЛЬНО понял.
Да. Да - ДЕСЯТЬ. Если я ВСЁ ПРАВИЛЬНО понял.
Заметки о тестировании, программировании и прочий "поток сознания", который жалко писать "в стол"
USES Documentation.ms.dict params.ms.dict core.ms.dict map.ms.dict ; : (string) ^ IN aValue aValue DO ToPrintable ; // (string) STRING FUNCTION string:CatWihAny STRING IN aString IN aValue aString aValue ToPrintable Cat =: Result ; // string:CatWihAny STRING FUNCTION any:Cat ARRAY IN anArray anArray .map> ToPrintable strings:Cat =: Result ; // any:Cat STRING FUNCTION (+)? STRING in aPrefix STRING right aSuffix %SUMMARY 'Если aSuffix не пустой, то возвращает сумму aPrefix и aSuffix, иначе возвращает пустую строку' ; Result := '' STRING VAR l_Suffix aSuffix =: l_Suffix if ( l_Suffix =/= '' ) then ( aPrefix l_Suffix Cat =: Result ) ; // (+)? STRING FUNCTION ?(+) STRING in aPrefix STRING right aSuffix %SUMMARY 'Если aPrefix не пустой, то возвращает сумму aPrefix и aSuffix, иначе возвращает пустую строку' ; Result := '' if ( aPrefix =/= '' ) then ( aPrefix aSuffix Cat =: Result ) ; // ?(+) STRING FUNCTION strings:CatSep> STRING right aSep ARRAY right aValues aValues aSep strings:CatSep =: Result ; // strings:CatSep> WordAlias CatSep> strings:CatSep>
USES string.ms.dict ; '' (+)? 'B' Print 'A' (+)? 'B' Print 'A' (+)? '' Print '------------------' Print '' ?(+) 'B' Print 'A' ?(+) 'B' Print 'A' ?(+) '' Print '------------------' Print strings:CatSep> ' ' [ 'A' 'B' ] Print strings:CatSep> ' ' [ 'A ' 'B' ] Print strings:CatSep> ' ' [ 'A ' ' B' ] Print strings:CatSep> ' ' [ 'A' ' B' ] Print strings:CatSep> ' ' [ '' 'B' ] Print strings:CatSep> ' ' [ 'A' '' ] Print '------------------' Print [ 'A' 123 'B' ] any:Cat Print '------------------' Print
B AB ------------------ AB A ------------------ A B A B A B A B B A ------------------ A123B ------------------
USES Documentation.ms.dict Object.ms.dict ; Test CodeGen %REMARK ' CodeGen - это функция в которой мы будем тестировать наш функционал ' USES Concrete.ms.model // - подключаем "конкретную модель" ; USES CodeDump.ms.dict // - тут подключаем словарь CodeDump.ms.dict, чтобы "увидеть" слово DumpElement ; this.method.addr DumpElement %REMARK ' - тут дампим элемент CodeGen и его содержимое в стандартное устройство вывода. Чисто для отладки того, что мы сейчас написали. ' USES Generation.ms.dict // - это для того, чтобы подключить CallGensList CommonLang.ms.tpl // - подключаем конкретные шаблоны кодогенерации ; help %REMARK ' Выводим всю доступную аксиоматику в стандартное устройство вывода. Чисто для отладки того, что мы сейчас написали. ' ( Project1 Project2 Project3 ) %REMARK 'Список всех наших корневых элементов (проектов)' ( .dump .pas .c++ .h .script ) %REMARK 'Список всех наших генераторов' CallGensList %REMARK '- запускаем список генераторов на списке "рутовых элементов" модели.' ; // CodeGen CodeGen
// CommonLang.ms.tpl // Шаблоны генерации для "общепринятых языков" USES Documentation.ms.dict params.ms.dict NoStrangeSymbols.ms.dict arrays.ms.dict ElementsRTTI.ms.dict Generation.ms.dict string.ms.dict ; STRING FUNCTION CatSepIndent> ARRAY right aValues CatSep> cIndentChar aValues =: Result ; // CatSepIndent> elem_proc DumpAsIs %SUMMARY 'Процедура печатающая содержимое элемента модели. Рекурсивно.' ; CatSepIndent> [ Self .Stereotypes .reverted> ==> .Name %REMARK 'Выводим стереотип элемента, рекурсивно' Self .Name %REMARK 'Выводим имя элемента' ] OutToFile 'Родители ' (+)? CatSepIndent> ( Self .Parents .reverted> .map> .Name ) ?OutToFile 'Наследуемые ' (+)? CatSepIndent> ( Self .Inherited .map> .Name ) ?OutToFile 'Реализуемые ' (+)? CatSepIndent> ( Self .Implements .map> .Name ) ?OutToFile Self .generate.children %REMARK 'Выводим детей элемента, тем же самым генератором' [ '; // ' Self .Name ] OutToFile %REMARK 'Выводим закрывающую скобку элемента' ; // DumpAsIs elem_generator dump %SUMMARY 'Генератор выводящий дамп элемента модели.' ; %GEN_PROPERTY Name 'dump' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // dump elem_generator pas %SUMMARY 'Генератор выводящий элементы модели в Паскаль.' ; %GEN_PROPERTY Name 'pas' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // pas elem_generator script %SUMMARY 'Генератор выводящий элементы модели в ms.script.' ; %GEN_PROPERTY Name 'ms.script' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // script elem_generator c++ %SUMMARY ' Генератор выводящий элементы модели в c++. Про файлы *.h мы потом поговорим отдельно. ' ; %GEN_PROPERTY Name 'cpp' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // c++ elem_generator h %SUMMARY ' Генератор выводящий элементы модели в *.h. Про файлы *.h мы потом поговорим отдельно. ' ; %GEN_PROPERTY Name 'h' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // h
USES metaMACRO.ms.dict classRelations.ms.dict EngineTypes.ms.dict Object.ms.dict ; Test CodeGen %REMARK ' CodeGen - это функция в которой мы будем тестировать наш функционал ' %REMARK ' %SUMMARY это мета-информация, которая позволяет привязывать документацию к элементам кода. Эта документация доступна потом из скриптовой машины. ' %SUMMARY ' Тут будем тестировать построение сначала мета-модели, потом модели, а потом и кодогенерации ' ; // %SUMMARY USES Templates.ms.model ; // --------------------------------------------------------------------------- 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 <<Program>> Program1 %SUMMARY ' Наверное наш проект реализует какую-то программу. Иначе - зачем бы он нам был бы нужен? Так вот Program1 - это программа внутри нашего проекта Project1. ' ; // %SUMMARY <<Class>> Class1 %SUMMARY ' Наверное наша программа содержит какие-то классы реализации. Иначе - кто будет реализовывать наш функционал? Так вот Class1 - это наш ПЕРВЫЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY ; // Class1 <<Interface>> Interface1 %SUMMARY ' Наверное наша программа настолько серьёзна, что реализует какие-то интерфейсы. Так вот Interface1 - это наш ПЕРВЫЙ интерфейс. ' ; // %SUMMARY ; // Interface1 <<Interface>> Interface2 %SUMMARY ' Наверное наша программа настолько серьёзна, что реализует НЕ ОДИН интерфейс, а несколько. Так вот Interface2 - это наш ВТОРОЙ интерфейс. ' ; // %SUMMARY ; // Interface2 <<Class>> Class2 %SUMMARY ' Наверное наша программа достаточно серьёзна и содержит НЕ ОДИН классы реализации. Так вот Class2 - это наш ВТОРОЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY %INHERITS Addr Class1 %REMARK 'Возможно наш проектный класс Class2 наследуется от класса Class1' ; // %INHERITS %IMPLEMENTS Addr Interface1 %REMARK 'Возможно наш проектный класс Class2 реализует интерфейс Interface1' Addr Interface2 %REMARK 'Возможно наш проектный класс Class2 реализует ещё и интерфейс Interface2' ; // %IMPLEMENTS ; // Class2 <<Class>> Class3 %SUMMARY ' Возможно, что у нас такая непростая программа, что в ней даже больше, чем ДВА класса реализации. Так вот Class3 - это наш ТРЕТИЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY ; // Class3 <<Class>> Class4 %SUMMARY ' Возможно, что мы настолько офигенно круты, что у на даже НЕ ТРИ класса реализации. Так вот Class4 - это наш ЧЕТВЁРТЫЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY %INHERITS Addr Class2 Addr Class3 %REMARK ' Возможно, что мы нстолько ОФИГЕННЫЕ перцы, что используем МНОЖЕСТВЕННОЕ наследование. И даже ПОНИМАЕМ - ЗАЧЕМ это нужно. Так вот Class4 - наследуется от Class2 и Class3. ' ; // %INHERITS ; // Class4 ; // Program1 ; // Project1 %REMARK ' РЕМАРКА. Все эти слова "наверное" вообще говоря должны проистекать из требований, ТЗ и UseCase Но мы про это позже поговорим. ' model-end // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- concrete-model-begin 'Модель конкретного проекта Project2' 'Тут будем определять аксиоматику конкретной модели конкретного проекта. А потом вынесем её в отдельный словарь. ' <<Project>> Project2 %SUMMARY ' Это наш ВТОРОЙ проект - Project2 ' ; // %SUMMARY ; // Project2 model-end // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- concrete-model-begin 'Модель конкретного проекта Project3' 'Тут будем определять аксиоматику конкретной модели конкретного проекта. А потом вынесем её в отдельный словарь. ' <<Project>> Project3 %SUMMARY ' Это наш ТРЕТИЙ проект - Project3 ' ; // %SUMMARY ; // Project3 model-end // --------------------------------------------------------------------------- USES CodeDump.ms.dict // - тут подключаем словарь CodeDump.ms.dict, чтобы "увидеть" слово DumpElement ; this.method.addr DumpElement %REMARK ' - тут дампим элемент CodeGen и его содержимое в стандартное устройство вывода. Чисто для отладки того, что мы сейчас написали. ' help %REMARK ' Выводим всю доступную аксиоматику в стандартное устройство вывода. Чисто для отладки того, что мы сейчас написали. ' USES Generation.ms.dict ; elem_proc DumpAsIs %SUMMARY 'Процедура печатающая содержимое элемента модели. Рекурсивно.' ; [ g_CurrentGeneratorName ':' %REMARK 'Выводим имя текущего генератора. Для отладки' for ( Self LIST %ST Reverted ) .Name %REMARK 'Выводим стереотип элемента, рекурсивно' Self .Name %REMARK 'Выводим имя элемента' ] ' ' strings:CatSep OutToFile [ 'Родители элемента ' for ( Self .Parents >reverted> ) .Name %REMARK 'Выводим родителей элемента, рекурсивно' ] '::' strings:CatSep OutToFile for ( Self .Inherited ) ( .Name OutToFile ) for ( Self .Implements ) ( .Name OutToFile ) TRY Self .CallChildrenCurrentGen %REMARK 'Выводим детей элемента, тем же самым генератором' FINALLY [ '; // ' Self .Name ] OutToFile %REMARK 'Выводим закрывающую скобку элемента' END ; // DumpAsIs elem_generator dump %SUMMARY 'Генератор выводящий дамп элемента модели.' ; %GEN_PROPERTY Name 'dump' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // dump elem_generator pas %SUMMARY 'Генератор выводящий элементы модели в Паскаль.' ; %GEN_PROPERTY Name 'pas' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // pas elem_generator script %SUMMARY 'Генератор выводящий элементы модели в ms.script.' ; %GEN_PROPERTY Name 'ms.script' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // script elem_generator c++ %SUMMARY ' Генератор выводящий элементы модели в c++. Про файлы *.h мы потом поговорим отдельно. ' ; %GEN_PROPERTY Name 'cpp' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // c++ elem_generator h %SUMMARY ' Генератор выводящий элементы модели в *.h. Про файлы *.h мы потом поговорим отдельно. ' ; %GEN_PROPERTY Name 'h' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // h ( Project1 Project2 Project3 ) %REMARK 'Список всех наших корневых элементов (проектов)' ( .dump .pas .c++ .h .script ) %REMARK 'Список всех наших генераторов' CallGensList %REMARK '- запускаем список генераторов на списке "рутовых элементов" модели.' ; // CodeGen CodeGen
USES metaMACRO.ms.dict classRelations.ms.dict EngineTypes.ms.dict Object.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>> Program %SUMMARY ' Наверное у нас при разработке встречаются программы. Так вот Program это стереотип, который описывает наши программы. ' ; // %SUMMARY ; // Program <<UMLClass>> Class %SUMMARY ' Наверное у нас при разработке встречаются проектные классы. Так вот Class это стереотип, который описывает наши проектные классы. ' ; // %SUMMARY ; // Class <<UMLClass>> Interface %SUMMARY ' Наверное у нас при разработке встречаются интерфейсы. Так вот Interface это стереотип, который описывает наши интерфейсы. ' ; // %SUMMARY ; // Interface %REMARK ' Могут ли Library вкладываться в Project, а Project в Library Или могут ли Program вкладываться в Class, а Class в Program И прочие отношения между стереотипами - мы определим несколько позже. Когда начнём использовать их. ' 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 <<Program>> Program1 %SUMMARY ' Наверное наш проект реализует какую-то программу. Иначе - зачем бы он нам был бы нужен? Так вот Program1 - это программа внутри нашего проекта Project1. ' ; // %SUMMARY <<Class>> Class1 %SUMMARY ' Наверное наша программа содержит какие-то классы реализации. Иначе - кто будет реализовывать наш функционал? Так вот Class1 - это наш ПЕРВЫЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY ; // Class1 <<Interface>> Interface1 %SUMMARY ' Наверное наша программа настолько серьёзна, что реализует какие-то интерфейсы. Так вот Interface1 - это наш ПЕРВЫЙ интерфейс. ' ; // %SUMMARY ; // Interface1 <<Interface>> Interface2 %SUMMARY ' Наверное наша программа настолько серьёзна, что реализует НЕ ОДИН интерфейс, а несколько. Так вот Interface2 - это наш ВТОРОЙ интерфейс. ' ; // %SUMMARY ; // Interface2 <<Class>> Class2 %SUMMARY ' Наверное наша программа достаточно серьёзна и содержит НЕ ОДИН классы реализации. Так вот Class2 - это наш ВТОРОЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY %INHERITS Addr Class1 %REMARK 'Возможно наш проектный класс Class2 наследуется от класса Class1' ; // %INHERITS %IMPLEMENTS Addr Interface1 %REMARK 'Возможно наш проектный класс Class2 реализует интерфейс Interface1' Addr Interface2 %REMARK 'Возможно наш проектный класс Class2 реализует ещё и интерфейс Interface2' ; // %IMPLEMENTS ; // Class2 <<Class>> Class3 %SUMMARY ' Возможно, что у нас такая непростая программа, что в ней даже больше, чем ДВА класса реализации. Так вот Class3 - это наш ТРЕТИЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY ; // Class3 <<Class>> Class4 %SUMMARY ' Возможно, что мы настолько офигенно круты, что у на даже НЕ ТРИ класса реализации. Так вот Class4 - это наш ЧЕТВЁРТЫЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY %INHERITS Addr Class2 Addr Class3 %REMARK ' Возможно, что мы нстолько ОФИГЕННЫЕ перцы, что используем МНОЖЕСТВЕННОЕ наследование. И даже ПОНИМАЕМ - ЗАЧЕМ это нужно. Так вот Class4 - наследуется от Class2 и Class3. ' ; // %INHERITS ; // Class4 ; // Program1 ; // Project1 %REMARK ' РЕМАРКА. Все эти слова "наверное" вообще говоря должны проистекать из требований, ТЗ и UseCase Но мы про это позже поговорим. ' model-end // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- concrete-model-begin 'Модель конкретного проекта Project2' 'Тут будем определять аксиоматику конкретной модели конкретного проекта. А потом вынесем её в отдельный словарь. ' <<Project>> Project2 %SUMMARY ' Это наш ВТОРОЙ проект - Project2 ' ; // %SUMMARY ; // Project2 model-end // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- concrete-model-begin 'Модель конкретного проекта Project3' 'Тут будем определять аксиоматику конкретной модели конкретного проекта. А потом вынесем её в отдельный словарь. ' <<Project>> Project3 %SUMMARY ' Это наш ТРЕТИЙ проект - Project3 ' ; // %SUMMARY ; // Project3 model-end // --------------------------------------------------------------------------- USES CodeDump.ms.dict // - тут подключаем словарь CodeDump.ms.dict, чтобы "увидеть" слово DumpElement ; this.method.addr DumpElement %REMARK ' - тут дампим элемент CodeGen и его содержимое в стандартное устройство вывода. Чисто для отладки того, что мы сейчас написали. ' help %REMARK ' Выводим всю доступную аксиоматику в стандартное устройство вывода. Чисто для отладки того, что мы сейчас написали. ' %REMARK ' Теперь, что мы можем сделать с нашим проектом? Ну для начала выведем его содержимое на стандартное устройство вывода. ' // --------------------------------------------------------------------------- %REMARK 'Это всё хозяйство надо будет потом выделить в отдельный словарь' ENGINE_WORD TYPE ModelElement %REMARK 'Элемент модели' PROCEDURE do_elem_func STRING IN aName ENGINE_WORD IN aSelf ENGINE_WORD 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 .Name Ctx:Parser:PushSymbol ) axiom:PushSymbol in 'Self' Ctx:Parser:PushSymbol ; // do_elem_func MACRO elem_func Literal IN aName %SUMMARY 'Функция на элементе модели' ; aName .Name this.method.addr nil do_elem_func ; // elem_func PROCEDURE do_elem_proc STRING IN aName ENGINE_WORD IN aSelf ENGINE_WORD 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 .Name this.method.addr nil do_elem_proc ; // elem_proc MACRO elem_generator Literal IN aName %SUMMARY 'Генератор содержимого элемента' ; aName .Name this.method.addr nil do_elem_proc ; // elem_generator MACRO elem_ref_proc Literal IN aName %SUMMARY 'Процедура на элементе модели, который передаётся по ссылке' ; aName .Name this.method.addr Addr LVALUE_MOD do_elem_proc ; // elem_ref_proc BOOLEAN elem_func IsSummary %SUMMARY ' Определяет тот факт, что aWord является документацией к элементу ' ; // %SUMMARY ( Self .Name ) = '%SUM' =: Result ; // IsSummary BOOLEAN elem_func IsModelElement %SUMMARY ' Определяет тот факт, что aWord является "элементом модели" ' ; // %SUMMARY '<<' Self %ST .Name StartsStr AND ( '>>' Self %ST .Name EndsStr ) =: Result //NOT ( Self .IsSummary ) //=: Result ; // IsModelElement ARRAY elem_func ElementList Literal IN aListName %SUMMARY 'Возвращает итератор именованного списка на элементе' ; private BOOLEAN FUNCTION FilterElement OBJECT IN aMember Result := ( aMember NotValid ! ) ; // FilterElement private OBJECT FUNCTION MapElement OBJECT IN aMember VAR l_Element aMember DO =: l_Element // - берём ссылку на элемент модели if ( l_Element IsVoid ) then // - пропускаем невалидные ссылки ( Result := nil ) else ( Result := l_Element ) ; // MapElement if ( Self NotValid ) then ( Result := [ ] ) else ( VAR l_List l_List := ( Self %% ( aListName .Name ) ) if ( l_List NotValid ) then ( Result := [ ] ) else ( Result := ( ( l_List CodeIterator ) >map> MapElement >filter> FilterElement ) ) ) ; // ElementList ARRAY elem_func Implements %SUMMARY 'Возвращает итератор элементов которые реализуют наш элемент' ; Result := ( Self .ElementList %R ) ; ARRAY elem_func Inherited %SUMMARY 'Возвращает итератор элементов от которого наследуется наш элемент' ; Result := ( Self .ElementList %G ) ; ARRAY elem_func Children %SUMMARY 'Возвращает итератор детей Self в "терминах определённой модели"' ; ( Self MembersIterator ) >filter> .IsModelElement =: Result ; // Children ARRAY elem_func Parents %SUMMARY 'Возвращает итератор родителей aWord в "терминах определённой модели"' ; ( Self LIST %P ) >filter> .IsModelElement =: Result ; // Parents 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 'Генератор содержимого элемента' GENERATOR VAR g_CurrentGenerator %REMARK 'Текущий генератор' g_CurrentGenerator := nil STRING VAR g_CurrentGeneratorName %REMARK 'Имя текущего генератора' g_CurrentGeneratorName := '' STRING FUNCTION OutFileName STRING right aGeneratorName %SUMMARY 'Имя файла для вывода' ; STRING VAR l_OutPath %REMARK 'Путь для вывода' sysutils:GetCurrentDir =: l_OutPath [ l_OutPath script:FileName %REMARK 'Путь к текущему скрипту' sysutils:ExtractFileName %REMARK 'Вырезаем из пути только имя файла' '' sysutils:ChangeFileExt %REMARK 'Убираем .script' '' sysutils:ChangeFileExt %REMARK 'Убираем .ms' ] '\' strings:CatSep =: l_OutPath l_OutPath sysutils:ForceDirectories ?ASSURE [ 'Не удалось создать директорию ' l_OutPath ] %REMARK 'Создаём директорию рекурсивно, если её ещё не было' [ l_OutPath aGeneratorName ] '\' strings:CatSep =: Result ; // OutFileName elem_proc CallCurrentGen FILE VAR l_PrevOutFile l_PrevOutFile := g_OutFile TRY g_OutFile := ( OutFileName ( Self .Name (+) '.' (+) g_CurrentGeneratorName ) File:OpenWrite ) TRY g_CurrentGenerator IsNil ! ?ASSURE 'Текущй генератор пустой' Self ( g_CurrentGenerator DO ) %REMARK 'Вызываем на элементе генератор g_CurrentGenerator' FINALLY g_OutFile := nil END // TRY..FINALLY FINALLY g_OutFile := l_PrevOutFile l_PrevOutFile := nil END ; // CallCurrentGen elem_proc Child.CallCurrentGen %SUMMARY 'Вызывает на ДОЧЕРНЕМ элементе генератор g_CurrentGenerator с учётом отступов' ; Self .EnterElement TRY Self .CallCurrentGen //Self g_CurrentGenerator DO %REMARK 'Вызываем генератор g_CurrentGenerator' FINALLY Self .LeaveElement END // TRY..FINALLY ; // Child.CallCurrentGen elem_proc CallChildrenCurrentGen %SUMMARY 'Вызывает текущий генератор для всех детей элемента модели' ; for ( Self .Children ) .Child.CallCurrentGen ; // CallChildrenCurrentGen CONST GEN_PROPERTY_PREFIX 'gp' %REMARK 'Префикс имени свойства генератора' MACRO %GEN_PROPERTY Literal IN aName %SUMMARY 'Свойство генератора' ; this.method.addr Ctx:SetWordProducerForCompiledClass axiom:PushSymbol CONST GEN_PROPERTY_PREFIX (+) ( aName .Name ) Ctx:Parser:PushSymbol ; // %GEN_PROPERTY USES RefDeepest.ms.dict ; REF operator FieldByNameDeepest LVALUE aSelf Literal IN aName %SUMMARY 'Метод получения вложенного члена слова по имени. С учётом того, что может быть передана ссылка на переменную' ; ENGINE_WORD VAR l_Self aSelf Dereference =: l_Self STRING VAR l_Name aName .Name =: l_Name OBJECT VAR l_Res l_Self %% l_Name =: l_Res ASSURE NOT ( l_Res pop:object:IsNil ) [ 'Не найдено поле: ' l_Self LIST %P Reverted ==> ( .Name '::' ) l_Self .Name '::' l_Name ] l_Res =: Result ; // FieldByNameDeepest MACRO %GP Literal IN aName %SUMMARY 'Метод получения свойства генератора' ; axiom:PushSymbol FieldByNameDeepest GEN_PROPERTY_PREFIX (+) ( aName .Name ) Ctx:Parser:PushSymbol ; // %GP elem_proc CallGen GENERATOR RIGHT IN aGen %SUMMARY ' Вызывает на элементе генератор aGen. С открытием "правильных файлов". ' ; // %SUMMARY aGen Dereference =: g_CurrentGenerator TRY aGen %GP Name =: g_CurrentGeneratorName Self .CallCurrentGen FINALLY g_CurrentGenerator := nil END ; // CallGen PROCEDURE CallGens ARRAY IN anElements ARRAY IN aGenerators %SUMMARY 'Вызывает все определённые генераторы на элементах массива anElements' ; for anElements ( ModelElement IN anElement for aGenerators ( GENERATOR IN aGen anElement .CallGen aGen %REMARK 'Вызываем на элементе anElement генератор aGen' ) // for aGenerators ) // for anElements ; // CallGens PROCEDURE CallGensList Sequence LVALUE anElements Sequence LVALUE aGenerators ( anElements CodeIterator ) ( aGenerators CodeIterator ) CallGens ; // CallGensList // --------------------------------------------------------------------------- elem_proc DumpAsIs %SUMMARY ' Процедура печатающая содержимое элемента модели. Рекурсивно. ' ; // %SUMMARY [ g_CurrentGeneratorName ':' %REMARK 'Выводим имя текущего генератора. Для отладки' for ( Self LIST %ST Reverted ) .Name %REMARK 'Выводим стереотип элемента, рекурсивно' Self .Name %REMARK 'Выводим имя элемента' ] ' ' strings:CatSep OutToFile [ 'Родители элемента ' for ( Self .Parents >reverted> ) .Name %REMARK 'Выводим родителей элемента, рекурсивно' ] '::' strings:CatSep OutToFile for ( Self .Inherited ) ( .Name OutToFile ) for ( Self .Implements ) ( .Name OutToFile ) TRY Self .CallChildrenCurrentGen %REMARK 'Выводим детей элемента, тем же самым генератором' FINALLY [ '; // ' Self .Name ] OutToFile %REMARK 'Выводим закрывающую скобку элемента' END ; // DumpAsIs elem_generator dump %SUMMARY 'Генератор выводящий дамп элемента модели.' ; %GEN_PROPERTY Name 'dump' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // dump elem_generator pas %SUMMARY 'Генератор выводящий элементы модели в Паскаль.' ; %GEN_PROPERTY Name 'pas' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // pas elem_generator c++ %SUMMARY ' Генератор выводящий элементы модели в c++. Про файлы *.h мы потом поговорим отдельно. ' ; %GEN_PROPERTY Name 'cpp' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // c++ elem_generator h %SUMMARY ' Генератор выводящий элементы модели в *.h. Про файлы *.h мы потом поговорим отдельно. ' ; %GEN_PROPERTY Name 'h' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // h ( Project1 Project2 Project3 ) %REMARK 'Список всех наших корневых элементов (проектов)' ( .dump .pas .c++ .h ) %REMARK 'Список всех наших генераторов' CallGensList %REMARK '- запускаем список генераторов на списке "рутовых элементов" модели.' ; // CodeGen CodeGen
USES metaMACRO.ms.dict classRelations.ms.dict EngineTypes.ms.dict Object.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>> Program %SUMMARY ' Наверное у нас при разработке встречаются программы. Так вот Program это стереотип, который описывает наши программы. ' ; // %SUMMARY ; // Program <<UMLClass>> Class %SUMMARY ' Наверное у нас при разработке встречаются проектные классы. Так вот Class это стереотип, который описывает наши проектные классы. ' ; // %SUMMARY ; // Class <<UMLClass>> Interface %SUMMARY ' Наверное у нас при разработке встречаются интерфейсы. Так вот Interface это стереотип, который описывает наши интерфейсы. ' ; // %SUMMARY ; // Interface %REMARK ' Могут ли Library вкладываться в Project, а Project в Library Или могут ли Program вкладываться в Class, а Class в Program И прочие отношения между стереотипами - мы определим несколько позже. Когда начнём использовать их. ' 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 <<Program>> Program1 %SUMMARY ' Наверное наш проект реализует какую-то программу. Иначе - зачем бы он нам был бы нужен? Так вот Program1 - это программа внутри нашего проекта Project1. ' ; // %SUMMARY <<Class>> Class1 %SUMMARY ' Наверное наша программа содержит какие-то классы реализации. Иначе - кто будет реализовывать наш функционал? Так вот Class1 - это наш ПЕРВЫЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY ; // Class1 <<Interface>> Interface1 %SUMMARY ' Наверное наша программа настолько серьёзна, что реализует какие-то интерфейсы. Так вот Interface1 - это наш ПЕРВЫЙ интерфейс. ' ; // %SUMMARY ; // Interface1 <<Interface>> Interface2 %SUMMARY ' Наверное наша программа настолько серьёзна, что реализует НЕ ОДИН интерфейс, а несколько. Так вот Interface2 - это наш ВТОРОЙ интерфейс. ' ; // %SUMMARY ; // Interface2 <<Class>> Class2 %SUMMARY ' Наверное наша программа достаточно серьёзна и содержит НЕ ОДИН классы реализации. Так вот Class2 - это наш ВТОРОЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY %INHERITS Addr Class1 %REMARK 'Возможно наш проектный класс Class2 наследуется от класса Class1' ; // %INHERITS %IMPLEMENTS Addr Interface1 %REMARK 'Возможно наш проектный класс Class2 реализует интерфейс Interface1' Addr Interface2 %REMARK 'Возможно наш проектный класс Class2 реализует ещё и интерфейс Interface2' ; // %IMPLEMENTS ; // Class2 <<Class>> Class3 %SUMMARY ' Возможно, что у нас такая непростая программа, что в ней даже больше, чем ДВА класса реализации. Так вот Class3 - это наш ТРЕТИЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY ; // Class3 <<Class>> Class4 %SUMMARY ' Возможно, что мы настолько офигенно круты, что у на даже НЕ ТРИ класса реализации. Так вот Class4 - это наш ЧЕТВЁРТЫЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY %INHERITS Addr Class2 Addr Class3 %REMARK ' Возможно, что мы нстолько ОФИГЕННЫЕ перцы, что используем МНОЖЕСТВЕННОЕ наследование. И даже ПОНИМАЕМ - ЗАЧЕМ это нужно. Так вот Class4 - наследуется от Class2 и Class3. ' ; // %INHERITS ; // Class4 ; // Program1 ; // Project1 %REMARK ' РЕМАРКА. Все эти слова "наверное" вообще говоря должны проистекать из требований, ТЗ и UseCase Но мы про это позже поговорим. ' model-end // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- concrete-model-begin 'Модель конкретного проекта Project2' 'Тут будем определять аксиоматику конкретной модели конкретного проекта. А потом вынесем её в отдельный словарь. ' <<Project>> Project2 %SUMMARY ' Это наш ВТОРОЙ проект - Project2 ' ; // %SUMMARY ; // Project2 model-end // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- concrete-model-begin 'Модель конкретного проекта Project3' 'Тут будем определять аксиоматику конкретной модели конкретного проекта. А потом вынесем её в отдельный словарь. ' <<Project>> Project3 %SUMMARY ' Это наш ТРЕТИЙ проект - Project3 ' ; // %SUMMARY ; // Project3 model-end // --------------------------------------------------------------------------- USES CodeDump.ms.dict // - тут подключаем словарь CodeDump.ms.dict, чтобы "увидеть" слово DumpElement ; this.method.addr DumpElement %REMARK ' - тут дампим элемент CodeGen и его содержимое в стандартное устройство вывода. Чисто для отладки того, что мы сейчас написали. ' help %REMARK ' Выводим всю доступную аксиоматику в стандартное устройство вывода. Чисто для отладки того, что мы сейчас написали. ' %REMARK ' Теперь, что мы можем сделать с нашим проектом? Ну для начала выведем его содержимое на стандартное устройство вывода. ' // --------------------------------------------------------------------------- %REMARK 'Это всё хозяйство надо будет потом выделить в отдельный словарь' ENGINE_WORD TYPE ModelElement %REMARK 'Элемент модели' PROCEDURE do_elem_func STRING IN aName ENGINE_WORD IN aSelf ENGINE_WORD 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 .Name Ctx:Parser:PushSymbol ) axiom:PushSymbol in 'Self' Ctx:Parser:PushSymbol ; // do_elem_func MACRO elem_func Literal IN aName %SUMMARY 'Функция на элементе модели' ; aName .Name this.method.addr nil do_elem_func ; // elem_func PROCEDURE do_elem_proc STRING IN aName ENGINE_WORD IN aSelf ENGINE_WORD 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 .Name this.method.addr nil do_elem_proc ; // elem_proc MACRO elem_generator Literal IN aName %SUMMARY 'Генератор содержимого элемента' ; aName .Name this.method.addr nil do_elem_proc ; // elem_generator MACRO elem_ref_proc Literal IN aName %SUMMARY 'Процедура на элементе модели, который передаётся по ссылке' ; aName .Name this.method.addr Addr LVALUE_MOD do_elem_proc ; // elem_ref_proc BOOLEAN elem_func IsSummary %SUMMARY ' Определяет тот факт, что aWord является документацией к элементу ' ; // %SUMMARY ( Self .Name ) = '%SUM' =: Result ; // IsSummary BOOLEAN elem_func IsModelElement %SUMMARY ' Определяет тот факт, что aWord является "элементом модели" ' ; // %SUMMARY '<<' Self %ST .Name StartsStr AND ( '>>' Self %ST .Name EndsStr ) =: Result //NOT ( Self .IsSummary ) //=: Result ; // IsModelElement ARRAY elem_func Children %SUMMARY ' Возвращает итератор детей aWord в "терминах определённой модели" ' ; ( Self MembersIterator ) >filter> .IsModelElement =: Result ; // Children ARRAY elem_func Parents %SUMMARY ' Возвращает итератор родителей aWord в "терминах определённой модели" ' ; ( Self LIST %P ) >filter> .IsModelElement =: Result ; // Parents 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 'Генератор содержимого элемента' GENERATOR VAR g_CurrentGenerator %REMARK 'Текущий генератор' g_CurrentGenerator := nil elem_proc Child.CallCurrentGen %SUMMARY 'Вызывает на ДОЧЕРНЕМ элементе генератор g_CurrentGenerator с учётом отступов' ; Self .EnterElement TRY Self g_CurrentGenerator DO %REMARK 'Вызываем генератор g_CurrentGenerator' FINALLY Self .LeaveElement END // TRY..FINALLY ; // Child.CallCurrentGen elem_proc CallChildrenCurrentGen %SUMMARY 'Вызывает текущий генератор для всех детей элемента модели' ; for ( Self .Children ) .Child.CallCurrentGen ; // CallChildrenCurrentGen CONST GEN_PROPERTY_PREFIX 'gp' %REMARK 'Префикс имени свойства генератора' MACRO %GEN_PROPERTY Literal IN aName %SUMMARY 'Свойство генератора' ; this.method.addr Ctx:SetWordProducerForCompiledClass axiom:PushSymbol CONST GEN_PROPERTY_PREFIX (+) ( aName .Name ) Ctx:Parser:PushSymbol ; // %GEN_PROPERTY USES RefDeepest.ms.dict ; REF operator FieldByNameDeepest LVALUE aSelf Literal IN aName %SUMMARY 'Метод получения вложенного члена слова по имени. С учётом того, что может быть передана ссылка на переменную' ; ENGINE_WORD VAR l_Self aSelf GetRefDeepestFromLeft =: l_Self STRING VAR l_Name aName .Name =: l_Name OBJECT VAR l_Res l_Self %% l_Name =: l_Res ASSURE NOT ( l_Res pop:object:IsNil ) [ 'Не найдено поле: ' l_Self LIST %P Reverted ==> ( .Name '::' ) l_Self .Name '::' l_Name ] l_Res =: Result ; // FieldByNameDeepest MACRO %GP Literal IN aName %SUMMARY 'Метод получения свойства генератора' ; axiom:PushSymbol FieldByNameDeepest GEN_PROPERTY_PREFIX (+) ( aName .Name ) Ctx:Parser:PushSymbol ; // %GP STRING FUNCTION OutFileName STRING right aGeneratorName %SUMMARY 'Имя файла для вывода' ; STRING VAR l_OutPath %REMARK 'Путь для вывода' sysutils:GetCurrentDir =: l_OutPath [ l_OutPath script:FileName %REMARK 'Путь к текущему скрипту' sysutils:ExtractFileName %REMARK 'Вырезаем из пути только имя файла' '' sysutils:ChangeFileExt %REMARK 'Убираем .script' '' sysutils:ChangeFileExt %REMARK 'Убираем .ms' ] '\' strings:CatSep =: l_OutPath l_OutPath sysutils:ForceDirectories ?ASSURE [ 'Не удалось создать директорию ' l_OutPath ] %REMARK 'Создаём директорию рекурсивно, если её ещё не было' [ l_OutPath aGeneratorName ] '\' strings:CatSep =: Result ; // OutFileName STRING VAR g_CurrentGeneratorName %REMARK 'Имя текущего генератора' g_CurrentGeneratorName := '' elem_proc CallGen GENERATOR RIGHT IN aGen %SUMMARY ' Вызывает на элементе генератор aGen. С открытием "правильных файлов". ' ; // %SUMMARY aGen %GP Name =: g_CurrentGeneratorName g_OutFile := ( OutFileName ( Self .Name (+) '.' (+) g_CurrentGeneratorName ) File:OpenWrite ) TRY g_CurrentGenerator := ( aGen GetRefDeepestFromLeft ) TRY g_CurrentGenerator .Name Print Self ( aGen GetRefDeepestFromLeft DO ) %REMARK 'Вызываем на элементе генератор aGen' FINALLY g_CurrentGenerator := nil END // TRY..FINALLY FINALLY g_OutFile := nil END // TRY..FINALLY ; // CallGen PROCEDURE CallGens ARRAY IN anElements ARRAY IN aGenerators %SUMMARY 'Вызывает все определённые генераторы на элементах массива anElements' ; for anElements ( ModelElement IN anElement for aGenerators ( GENERATOR IN aGen anElement .CallGen aGen %REMARK 'Вызываем на элементе anElement генератор aGen' ) // for aGenerators ) // for anElements ; // CallGens PROCEDURE CallGensList Sequence LVALUE anElements Sequence LVALUE aGenerators ( anElements CodeIterator ) ( aGenerators CodeIterator ) CallGens ; // CallGensList // --------------------------------------------------------------------------- elem_proc DumpAsIs %SUMMARY ' Процедура печатающая содержимое элемента модели. Рекурсивно. ' ; // %SUMMARY [ g_CurrentGeneratorName ':' %REMARK 'Выводим имя текущего генератора. Для отладки' for ( Self LIST %ST Reverted ) .Name %REMARK 'Выводим стереотип элемента, рекурсивно' Self .Name %REMARK 'Выводим имя элемента' ] ' ' strings:CatSep OutToFile [ 'Родители элемента ' for ( Self .Parents >reverted> ) .Name %REMARK 'Выводим родителей элемента, рекурсивно' ] '::' strings:CatSep OutToFile TRY Self .CallChildrenCurrentGen %REMARK 'Выводим детей элемента, тем же самым генератором' FINALLY [ '; // ' Self .Name ] OutToFile %REMARK 'Выводим закрывающую скобку элемента' END ; // DumpAsIs elem_generator dump %SUMMARY 'Генератор выводящий дамп элемента модели.' ; %GEN_PROPERTY Name 'dump' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // dump elem_generator pas %SUMMARY 'Генератор выводящий элементы модели в Паскаль.' ; %GEN_PROPERTY Name 'pas' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // pas elem_generator c++ %SUMMARY ' Генератор выводящий элементы модели в c++. Про файлы *.h мы потом поговорим отдельно. ' ; %GEN_PROPERTY Name 'cpp' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // c++ elem_generator h %SUMMARY ' Генератор выводящий элементы модели в *.h. Про файлы *.h мы потом поговорим отдельно. ' ; %GEN_PROPERTY Name 'h' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // h ( Project1 Project2 Project3 ) %REMARK 'Список всех наших корневых элементов (проектов)' ( .dump .pas .c++ .h ) %REMARK 'Список всех наших генераторов' CallGensList %REMARK '- запускаем список генераторов на списке "рутовых элементов" модели.' ; // CodeGen CodeGen
[ Addr Project1 Addr Project2 Addr Project3 ] %REMARK 'Список всех наших корневых элементов (проектов)' [ Addr .dump Addr .pas Addr .c++ Addr .h ] %REMARK 'Список всех наших генераторов' CallGensList %REMARK '- запускаем список генераторов на списке "рутовых элементов" модели.'
( Project1 Project2 Project3 ) %REMARK 'Список всех наших корневых элементов (проектов)' ( .dump .pas .c++ .h ) %REMARK 'Список всех наших генераторов' CallGensList %REMARK '- запускаем список генераторов на списке "рутовых элементов" модели.'
Projects %REMARK 'Список всех наших корневых элементов (проектов)' Generators %REMARK 'Список всех наших генераторов' CallGens %REMARK '- запускаем список генераторов на списке "рутовых элементов" модели.'
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>> Program %SUMMARY ' Наверное у нас при разработке встречаются программы. Так вот Program это стереотип, который описывает наши программы. ' ; // %SUMMARY ; // Program <<UMLClass>> Class %SUMMARY ' Наверное у нас при разработке встречаются проектные классы. Так вот Class это стереотип, который описывает наши проектные классы. ' ; // %SUMMARY ; // Class <<UMLClass>> Interface %SUMMARY ' Наверное у нас при разработке встречаются интерфейсы. Так вот Interface это стереотип, который описывает наши интерфейсы. ' ; // %SUMMARY ; // Interface %REMARK ' Могут ли Library вкладываться в Project, а Project в Library Или могут ли Program вкладываться в Class, а Class в Program И прочие отношения между стереотипами - мы определим несколько позже. Когда начнём использовать их. ' 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 <<Program>> Program1 %SUMMARY ' Наверное наш проект реализует какую-то программу. Иначе - зачем бы он нам был бы нужен? Так вот Program1 - это программа внутри нашего проекта Project1. ' ; // %SUMMARY <<Class>> Class1 %SUMMARY ' Наверное наша программа содержит какие-то классы реализации. Иначе - кто будет реализовывать наш функционал? Так вот Class1 - это наш ПЕРВЫЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY ; // Class1 <<Interface>> Interface1 %SUMMARY ' Наверное наша программа настолько серьёзна, что реализует какие-то интерфейсы. Так вот Interface1 - это наш ПЕРВЫЙ интерфейс. ' ; // %SUMMARY ; // Interface1 <<Interface>> Interface2 %SUMMARY ' Наверное наша программа настолько серьёзна, что реализует НЕ ОДИН интерфейс, а несколько. Так вот Interface2 - это наш ВТОРОЙ интерфейс. ' ; // %SUMMARY ; // Interface2 <<Class>> Class2 %SUMMARY ' Наверное наша программа достаточно серьёзна и содержит НЕ ОДИН классы реализации. Так вот Class2 - это наш ВТОРОЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY %INHERITS @ Class1 %REMARK 'Возможно наш проектный класс Class2 наследуется от класса Class1' ; // %INHERITS %IMPLEMENTS @ Interface1 %REMARK 'Возможно наш проектный класс Class2 реализует интерфейс Interface1' @ Interface2 %REMARK 'Возможно наш проектный класс Class2 реализует ещё и интерфейс Interface2' ; // %IMPLEMENTS ; // Class2 <<Class>> Class3 %SUMMARY ' Возможно, что у нас такая непростая программа, что в ней даже больше, чем ДВА класса реализации. Так вот Class3 - это наш ТРЕТИЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY ; // Class3 <<Class>> Class4 %SUMMARY ' Возможно, что мы настолько офигенно круты, что у на даже НЕ ТРИ класса реализации. Так вот Class4 - это наш ЧЕТВЁРТЫЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY %INHERITS @ Class2 @ Class3 %REMARK ' Возможно, что мы нстолько ОФИГЕННЫЕ перцы, что используем МНОЖЕСТВЕННОЕ наследование. И даже ПОНИМАЕМ - ЗАЧЕМ это нужно. Так вот Class4 - наследуется от Class2 и Class3. ' ; // %INHERITS ; // Class4 ; // Program1 ; // Project1 %REMARK ' РЕМАРКА. Все эти слова "наверное" вообще говоря должны проистекать из требований, ТЗ и UseCase Но мы про это позже поговорим. ' model-end // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- concrete-model-begin 'Модель конкретного проекта Project2' 'Тут будем определять аксиоматику конкретной модели конкретного проекта. А потом вынесем её в отдельный словарь. ' <<Project>> Project2 %SUMMARY ' Это наш ВТОРОЙ проект - Project2 ' ; // %SUMMARY ; // Project2 model-end // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- concrete-model-begin 'Модель конкретного проекта Project3' 'Тут будем определять аксиоматику конкретной модели конкретного проекта. А потом вынесем её в отдельный словарь. ' <<Project>> Project3 %SUMMARY ' Это наш ТРЕТИЙ проект - Project3 ' ; // %SUMMARY ; // Project3 model-end // --------------------------------------------------------------------------- USES CodeDump.ms.dict // - тут подключаем словарь CodeDump.ms.dict, чтобы "увидеть" слово DumpElement ; @SELF DumpElement %REMARK ' - тут дампим элемент CodeGen и его содержимое в стандартное устройство вывода. Чисто для отладки того, что мы сейчас написали. ' help %REMARK ' Выводим всю доступную аксиоматику в стандартное устройство вывода. Чисто для отладки того, что мы сейчас написали. ' %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 CONST GEN_PROPERTY_PREFIX 'gp' %REMARK 'Префикс имени свойства генератора' MACRO %GEN_PROPERTY Literal IN aName %SUMMARY 'Свойство генератора' ; @SELF Ctx:SetWordProducerForCompiledClass axiom:PushSymbol CONST GEN_PROPERTY_PREFIX (+) ( aName |N ) Ctx:Parser:PushSymbol ; // %GEN_PROPERTY TtfwWord OPERATOR pop:Word:GetRefDeepest TtfwWord IN aSelf %SUMMARY ' Метод получения ссылки из aSelf, с учётом того, что могут передавать переменные. Аналог pop:Word:GetRefDeep. ' ; aSelf pop:Word:GetRefDeep @ Result pop:Word:SetValue if ( class::TkwCompiledVar Result pop:object:Inherits ) %REMARK 'Если это переменная, то ...' ( Result DO @SELF DO @ Result pop:Word:SetValue %REMARK '... разыменовываем её рекурсивно' ) ; // pop:Word:GetRefDeepest TtfwWord OPERATOR |RefDeepest ^@ IN aSelf %SUMMARY ' Метод получения ссылки из aSelf, с учётом того, что могут передавать переменные. Аналог |^@. ' ; aSelf pop:Word:GetRefDeepest @ Result pop:Word:SetValue ; // |RefDeepest ^@ operator FieldByNameDeepest ^@ IN aSelf ^L IN aName %SUMMARY 'Метод получения вложенного члена слова по имени. С учётом того, что может быть передана ссылка на переменную' ; TtfwWord VAR l_Self aSelf |RefDeepest >>> l_Self STRING VAR l_Name aName |N >>> l_Name OBJECT VAR l_Res l_Self %% l_Name >>> l_Res ASSURE NOT ( l_Res pop:object:IsNil ) [ 'Не найдено поле: ' l_Self LIST %P Reverted ==> ( |N '::' ) l_Self |N '::' l_Name ] l_Res >>> Result ; // FieldByNameDeepest MACRO %GP Literal IN aName %SUMMARY 'Метод получения свойства генератора' ; axiom:PushSymbol FieldByNameDeepest GEN_PROPERTY_PREFIX (+) ( aName |N ) Ctx:Parser:PushSymbol ; // %GP STRING FUNCTION OutFileName STRING right aGeneratorName %SUMMARY 'Имя файла для вывода' ; STRING VAR l_OutPath %REMARK 'Путь для вывода' sysutils:GetCurrentDir >>> l_OutPath [ l_OutPath script:FileName %REMARK 'Путь к текущему скрипту' sysutils:ExtractFileName %REMARK 'Вырезаем из пути только имя файла' '' sysutils:ChangeFileExt %REMARK 'Убираем .script' '' sysutils:ChangeFileExt %REMARK 'Убираем .ms' ] '\' strings:CatSep >>> l_OutPath l_OutPath sysutils:ForceDirectories ?ASSURE [ 'Не удалось создать директорию ' l_OutPath ] %REMARK 'Создаём директорию рекурсивно, если её ещё не было' [ l_OutPath aGeneratorName ] '\' strings:CatSep >>> Result ; // OutFileName STRING VAR g_CurrentGeneratorName %REMARK 'Имя текущего генератора' g_CurrentGeneratorName := '' elem_proc CallGen GENERATOR RIGHT IN aGen %SUMMARY ' Вызывает на элементе генератор aGen. С открытием "правильных файлов". ' ; // %SUMMARY aGen %GP Name >>> g_CurrentGeneratorName g_OutFile := ( OutFileName ( Self |N (+) '.' (+) g_CurrentGeneratorName ) File:OpenWrite ) TRY Self ( aGen |RefDeepest DO ) %REMARK 'Вызываем на элементе генератор aGen' FINALLY g_OutFile := nil END // TRY..FINALLY ; // CallGen PROCEDURE CallGens ARRAY IN anElements ARRAY IN aGenerators %SUMMARY 'Вызывает все определённые генераторы на элементах массива anElements' ; for anElements ( ModelElement IN anElement for aGenerators ( GENERATOR IN aGen anElement .CallGen aGen %REMARK 'Вызываем на элементе anElement генератор aGen' ) // for aGenerators ) // for anElements ; // CallGens // --------------------------------------------------------------------------- elem_proc DumpAsIs %SUMMARY ' Процедура печатающая содержимое элемента модели. Рекурсивно. ' ; // %SUMMARY [ g_CurrentGeneratorName ':' %REMARK 'Выводим имя текущего генератора. Для отладки' Self |S %REMARK 'Выводим стереотип элемента' Self |N %REMARK 'Выводим имя элемента' ] ' ' strings:CatSep OutToFile TRY for ( Self .Children ) .Child.CallGen call.me %REMARK 'Выводим детей элемента, тем же самым генератором' FINALLY [ '; // ' Self |N ] OutToFile %REMARK 'Выводим закрывающую скобку элемента' END ; // DumpAsIs elem_generator dump %SUMMARY 'Генератор выводящий дамп элемента модели.' ; %GEN_PROPERTY Name 'dump' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // dump elem_generator pas %SUMMARY 'Генератор выводящий элементы модели в Паскаль.' ; %GEN_PROPERTY Name 'pas' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // pas elem_generator c++ %SUMMARY ' Генератор выводящий элементы модели в c++. Про файлы *.h мы потом поговорим отдельно. ' ; %GEN_PROPERTY Name 'cpp' %REMARK 'Имя генератора и расширение файла целевого языка. Потом мы сделаем так, чтобы они могли не совпадать' Self .DumpAsIs %REMARK 'Пока выводим всё "как есть", без трансформации в целевой язык' ; // c++ ARRAY VAR Generators Generators := [ @ .dump @ .pas @ .c++ ] %REMARK 'Список всех наших генераторов' ARRAY VAR Projects Projects := [ @ Project1 @ Project2 @ Project3 ] %REMARK 'Список всех наших корневых элементов (проектов)' Projects %REMARK 'Список всех наших корневых элементов (проектов)' Generators %REMARK 'Список всех наших генераторов' CallGens %REMARK '- запускаем список генераторов на списке "рутовых элементов" модели.' ; // CodeGen CodeGen
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>> Program %SUMMARY ' Наверное у нас при разработке встречаются программы. Так вот Program это стереотип, который описывает наши программы. ' ; // %SUMMARY ; // Program <<UMLClass>> Class %SUMMARY ' Наверное у нас при разработке встречаются проектные классы. Так вот Class это стереотип, который описывает наши проектные классы. ' ; // %SUMMARY ; // Class <<UMLClass>> Interface %SUMMARY ' Наверное у нас при разработке встречаются интерфейсы. Так вот Interface это стереотип, который описывает наши интерфейсы. ' ; // %SUMMARY ; // Interface %REMARK ' Могут ли Library вкладываться в Project, а Project в Library Или могут ли Program вкладываться в Class, а Class в Program И прочие отношения между стереотипами - мы определим несколько позже. Когда начнём использовать их. ' 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 <<Program>> Program1 %SUMMARY ' Наверное наш проект реализует какую-то программу. Иначе - зачем бы он нам был бы нужен? Так вот Program1 - это программа внутри нашего проекта Project1. ' ; // %SUMMARY <<Class>> Class1 %SUMMARY ' Наверное наша программа содержит какие-то классы реализации. Иначе - кто будет реализовывать наш функционал? Так вот Class1 - это наш ПЕРВЫЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY ; // Class1 <<Interface>> Interface1 %SUMMARY ' Наверное наша программа настолько серьёзна, что реализует какие-то интерфейсы. Так вот Interface1 - это наш ПЕРВЫЙ интерфейс. ' ; // %SUMMARY ; // Interface1 <<Interface>> Interface2 %SUMMARY ' Наверное наша программа настолько серьёзна, что реализует НЕ ОДИН интерфейс, а несколько. Так вот Interface2 - это наш ВТОРОЙ интерфейс. ' ; // %SUMMARY ; // Interface2 <<Class>> Class2 %SUMMARY ' Наверное наша программа достаточно серьёзна и содержит НЕ ОДИН классы реализации. Так вот Class2 - это наш ВТОРОЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY %INHERITS @ Class1 %REMARK 'Возможно наш проектный класс Class2 наследуется от класса Class1' ; // %INHERITS %IMPLEMENTS @ Interface1 %REMARK 'Возможно наш проектный класс Class2 реализует интерфейс Interface1' @ Interface2 %REMARK 'Возможно наш проектный класс Class2 реализует ещё и интерфейс Interface2' ; // %IMPLEMENTS ; // Class2 <<Class>> Class3 %SUMMARY ' Возможно, что у нас такая непростая программа, что в ней даже больше, чем ДВА класса реализации. Так вот Class3 - это наш ТРЕТИЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY ; // Class3 <<Class>> Class4 %SUMMARY ' Возможно, что мы настолько офигенно круты, что у на даже НЕ ТРИ класса реализации. Так вот Class4 - это наш ЧЕТВЁРТЫЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY %INHERITS @ Class2 @ Class3 %REMARK ' Возможно, что мы нстолько ОФИГЕННЫЕ перцы, что используем МНОЖЕСТВЕННОЕ наследование. И даже ПОНИМАЕМ - ЗАЧЕМ это нужно. Так вот Class4 - наследуется от Class2 и Class3. ' ; // %INHERITS ; // Class4 ; // Program1 ; // Project1 %REMARK ' РЕМАРКА. Все эти слова "наверное" вообще говоря должны проистекать из требований, ТЗ и UseCase Но мы про это позже поговорим. ' model-end // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- concrete-model-begin 'Модель конкретного проекта Project2' 'Тут будем определять аксиоматику конкретной модели конкретного проекта. А потом вынесем её в отдельный словарь. ' <<Project>> Project2 %SUMMARY ' Это наш ВТОРОЙ проект - Project2 ' ; // %SUMMARY ; // Project2 model-end // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- concrete-model-begin 'Модель конкретного проекта Project3' 'Тут будем определять аксиоматику конкретной модели конкретного проекта. А потом вынесем её в отдельный словарь. ' <<Project>> Project3 %SUMMARY ' Это наш ТРЕТИЙ проект - Project3 ' ; // %SUMMARY ; // Project3 model-end // --------------------------------------------------------------------------- USES CodeDump.ms.dict // - тут подключаем словарь CodeDump.ms.dict, чтобы "увидеть" слово DumpElement ; @SELF DumpElement %REMARK ' - тут дампим элемент CodeGen и его содержимое в стандартное устройство вывода. Чисто для отладки того, что мы сейчас написали. ' help %REMARK ' Выводим всю доступную аксиоматику в стандартное устройство вывода. Чисто для отладки того, что мы сейчас написали. ' %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 CONST GEN_PROPERTY_PREFIX 'gp' %REMARK 'Префикс имени свойства генератора' MACRO %GEN_PROPERTY Literal IN aName %SUMMARY 'Свойство генератора' ; @SELF Ctx:SetWordProducerForCompiledClass axiom:PushSymbol CONST GEN_PROPERTY_PREFIX (+) ( aName |N ) Ctx:Parser:PushSymbol ; // %GEN_PROPERTY MACRO %GP Literal IN aName %SUMMARY 'Метод получения свойства генератора' ; axiom:PushSymbol :: GEN_PROPERTY_PREFIX (+) ( aName |N ) Ctx:Parser:PushSymbol ; // %GP STRING FUNCTION OutFileName STRING right aGeneratorName %SUMMARY 'Имя файла для вывода' ; STRING VAR l_OutPath %REMARK 'Путь для вывода' sysutils:GetCurrentDir >>> l_OutPath [ l_OutPath script:FileName %REMARK 'Путь к текущему скрипту' sysutils:ExtractFileName %REMARK 'Вырезаем из пути только имя файла' '' sysutils:ChangeFileExt %REMARK 'Убираем .script' '' sysutils:ChangeFileExt %REMARK 'Убираем .ms' ] '\' strings:CatSep >>> l_OutPath l_OutPath sysutils:ForceDirectories ?ASSURE [ 'Не удалось создать директорию ' l_OutPath ] %REMARK 'Создаём директорию рекурсивно, если её ещё не было' [ l_OutPath aGeneratorName ] '\' strings:CatSep >>> Result ; // OutFileName STRING VAR g_CurrentGeneratorName %REMARK 'Имя текущего генератора' g_CurrentGeneratorName := '' elem_proc CallGen GENERATOR RIGHT IN aGen %SUMMARY ' Вызывает на элементе генератор aGen. С открытием "правильных файлов". ' ; // %SUMMARY aGen %GP Name >>> g_CurrentGeneratorName g_OutFile := ( OutFileName ( Self |N (+) '.' (+) g_CurrentGeneratorName ) File:OpenWrite ) TRY Self ( aGen DO ) %REMARK 'Вызываем на элементе генератор aGen' FINALLY g_OutFile := nil END // TRY..FINALLY ; // CallGen // --------------------------------------------------------------------------- elem_proc DumpAsIs %SUMMARY ' Процедура печатающая содержимое элемента модели. Рекурсивно. ' ; // %SUMMARY [ g_CurrentGeneratorName ':' %REMARK 'Выводим имя текущего генератора. Для отладки' Self |S %REMARK 'Выводим стереотип элемента' Self |N %REMARK 'Выводим имя элемента' ] ' ' strings:CatSep OutToFile TRY for ( Self .Children ) .Child.CallGen call.me %REMARK 'Выводим детей элемента' FINALLY [ '; // ' Self |N ] OutToFile %REMARK 'Выводим закрывающую скобку элемента' END ; // DumpAsIs elem_generator dump %SUMMARY 'Генератор выводящий дамп элемента модели.' ; %GEN_PROPERTY Name 'dump' Self .DumpAsIs ; // dump elem_generator pas %SUMMARY 'Генератор выводящий элементы модели в Паскаль.' ; %GEN_PROPERTY Name 'pas' Self .DumpAsIs ; // pas PROCEDURE CallGens ARRAY IN anElements %SUMMARY 'Вызывает все определённые генераторы на элементах массива anElements' ; for anElements ( ModelElement IN anElement anElement .CallGen .dump %REMARK 'Вызываем на элементе генератор .dump' anElement .CallGen .pas %REMARK 'Вызываем на элементе генератор .pas' ) ; // CallGens ARRAY VAR Generators Generators := [ @ .dump @ .pas ] ARRAY VAR Projects Projects := [ @ Project1 @ Project2 @ Project3 ] Projects CallGens %REMARK '- запускаем всё наше хозяйство на списке элементов модели.' ; // CodeGen CodeGen
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>> Program %SUMMARY ' Наверное у нас при разработке встречаются программы. Так вот Program это стереотип, который описывает наши программы. ' ; // %SUMMARY ; // Program <<UMLClass>> Class %SUMMARY ' Наверное у нас при разработке встречаются проектные классы. Так вот Class это стереотип, который описывает наши проектные классы. ' ; // %SUMMARY ; // Class <<UMLClass>> Interface %SUMMARY ' Наверное у нас при разработке встречаются интерфейсы. Так вот Interface это стереотип, который описывает наши интерфейсы. ' ; // %SUMMARY ; // Interface %REMARK ' Могут ли Library вкладываться в Project, а Project в Library Или могут ли Program вкладываться в Class, а Class в Program И прочие отношения между стереотипами - мы определим несколько позже. Когда начнём использовать их. ' 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 <<Program>> Program1 %SUMMARY ' Наверное наш проект реализует какую-то программу. Иначе - зачем бы он нам был бы нужен? Так вот Program1 - это программа внутри нашего проекта Project1. ' ; // %SUMMARY <<Class>> Class1 %SUMMARY ' Наверное наша программа содержит какие-то классы реализации. Иначе - кто будет реализовывать наш функционал? Так вот Class1 - это наш ПЕРВЫЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY ; // Class1 <<Interface>> Interface1 %SUMMARY ' Наверное наша программа настолько серьёзна, что реализует какие-то интерфейсы. Так вот Interface1 - это наш ПЕРВЫЙ интерфейс. ' ; // %SUMMARY ; // Interface1 <<Interface>> Interface2 %SUMMARY ' Наверное наша программа настолько серьёзна, что реализует НЕ ОДИН интерфейс, а несколько. Так вот Interface2 - это наш ВТОРОЙ интерфейс. ' ; // %SUMMARY ; // Interface2 <<Class>> Class2 %SUMMARY ' Наверное наша программа достаточно серьёзна и содержит НЕ ОДИН классы реализации. Так вот Class2 - это наш ВТОРОЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY %INHERITS @ Class1 %REMARK 'Возможно наш проектный класс Class2 наследуется от класса Class1' ; // %INHERITS %IMPLEMENTS @ Interface1 %REMARK 'Возможно наш проектный класс Class2 реализует интерфейс Interface1' @ Interface2 %REMARK 'Возможно наш проектный класс Class2 реализует ещё и интерфейс Interface2' ; // %IMPLEMENTS ; // Class2 <<Class>> Class3 %SUMMARY ' Возможно, что у нас такая непростая программа, что в ней даже больше, чем ДВА класса реализации. Так вот Class3 - это наш ТРЕТИЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY ; // Class3 <<Class>> Class4 %SUMMARY ' Возможно, что мы настолько офигенно круты, что у на даже НЕ ТРИ класса реализации. Так вот Class4 - это наш ЧЕТВЁРТЫЙ класс реализации внутри нашей программы Program1. ' ; // %SUMMARY %INHERITS @ Class2 @ Class3 %REMARK ' Возможно, что мы нстолько ОФИГЕННЫЕ перцы, что используем МНОЖЕСТВЕННОЕ наследование. И даже ПОНИМАЕМ - ЗАЧЕМ это нужно. Так вот Class4 - наследуется от Class2 и Class3. ' ; // %INHERITS ; // Class4 ; // Program1 ; // 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 ' Теперь, что мы можем сделать с нашим проектом? Ну для начала выведем его содержимое на стандартное устройство вывода. ' // --------------------------------------------------------------------------- %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 CONST GEN_PROPERTY_PREFIX 'gp' %REMARK 'Префикс имени свойства генератора' MACRO %GEN_PROPERTY Literal IN aName %SUMMARY 'Свойство генератора' ; @SELF Ctx:SetWordProducerForCompiledClass axiom:PushSymbol CONST GEN_PROPERTY_PREFIX (+) ( aName |N ) Ctx:Parser:PushSymbol ; // %GEN_PROPERTY MACRO %GP Literal IN aName %SUMMARY 'Метод получения свойства генератора' ; axiom:PushSymbol :: GEN_PROPERTY_PREFIX (+) ( aName |N ) Ctx:Parser:PushSymbol ; // %GP STRING FUNCTION OutFileName STRING right aGeneratorName %SUMMARY 'Имя файла для вывода' ; script:FileName sysutils:ExtractFileName (+) '.' (+) aGeneratorName >>> Result ; // OutFileName STRING VAR g_CurrentGeneratorName %REMARK 'Имя текущего генератора' g_CurrentGeneratorName := '' elem_proc CallGen GENERATOR RIGHT IN aGen %SUMMARY ' Вызывает на элементе генератор aGen. С открытием "правильных файлов". ' ; // %SUMMARY aGen %GP Name >>> g_CurrentGeneratorName g_OutFile := ( OutFileName ( Self |N (+) '.' (+) g_CurrentGeneratorName ) File:OpenWrite ) TRY Self ( aGen DO ) %REMARK 'Вызываем на элементе генератор aGen' FINALLY g_OutFile := nil END // TRY..FINALLY ; // CallGen // --------------------------------------------------------------------------- elem_proc DumpAsIs %SUMMARY ' Процедура печатающая содержимое элемента модели. Рекурсивно. ' ; // %SUMMARY [ g_CurrentGeneratorName ':' %REMARK 'Выводим имя текущего генератора. Для отладки' Self |S %REMARK 'Выводим стереотип элемента' Self |N %REMARK 'Выводим имя элемента' ] ' ' strings:CatSep OutToFile TRY for ( Self .Children ) .Child.CallGen call.me %REMARK 'Выводим детей элемента' FINALLY [ '; // ' Self |N ] OutToFile %REMARK 'Выводим закрывающую скобку элемента' END ; // DumpAsIs elem_generator dump %SUMMARY 'Генератор выводящий дамп элемента модели.' ; %GEN_PROPERTY Name 'dump' Self .DumpAsIs ; // dump elem_generator pas %SUMMARY 'Генератор выводящий элементы модели в Паскаль.' ; %GEN_PROPERTY Name 'pas' Self .DumpAsIs ; // pas PROCEDURE CallGens ARRAY IN anElements %SUMMARY 'Вызывает все определённые генераторы на элементах массива anElements' ; for anElements ( ModelElement IN anElement anElement .CallGen .dump %REMARK 'Вызываем на элементе генератор .dump' anElement .CallGen .pas %REMARK 'Вызываем на элементе генератор .pas' ) ; // CallGens ARRAY VAR Generators Generators := [ @ .dump @ .pas ] ARRAY VAR Projects Projects := [ @ Project1 @ Project2 ] Projects CallGens %REMARK '- запускаем всё наше хозяйство на списке элементов модели.' ; // CodeGen CodeGen