Предыдущая серия была тут - Кодогенерация. Вынесем конкретную модель и конкретные шаблоны во внешние словари.
Я ещё немного поработал и вот что пока получается:
Я ещё немного поработал и вот что пока получается:
// Generation.ms.dict USES Documentation.ms.dict EngineTypes.ms.dict axiom_push.ms.dict macro.ms.dict core.ms.dict NoStrangeSymbols.ms.dict params.ms.dict NoCapsLock.ms.dict arrays.ms.dict WordsRTTI.ms.dict ElementsRTTI.ms.dict string.ms.dict ; INTEGER VAR g_Indent %REMARK 'Текущий отступ' ( g_Indent := 0 ) BOOLEAN elem_func IsElementNeed.Indent %SUMMARY 'Определяет тот факт, что элементу нужен отступ' ; Result := true ; // IsElementNeed.Indent elem_proc EnterElement %SUMMARY 'Начинает вывод элемента' ; Self .IsElementNeed.Indent ? INC g_Indent ; // EnterElement elem_proc LeaveElement %SUMMARY 'Заканчивает вывод элемента' ; Self .IsElementNeed.Indent ? DEC g_Indent ; // LeaveElement FILE VAR g_OutFile ( g_OutFile := nil ) STRING INTEGER ARRAY TYPE OUTABLE CONST cIndentChar ' ' PROCEDURE OutToFile OUTABLE IN aValue %SUMMARY ' Выводит значение в текущий файл вывода. С переводом каретки. ' ; // %SUMMARY STRING VAR l_String if ( aValue IsArray ) then ( aValue strings:Cat =: l_String ) else ( aValue ToPrintable =: l_String ) [ g_Indent cIndentChar char:Dupe l_String ] strings:Cat g_OutFile File:WriteLn %REMARK '- выводим элементы модели в файл, а не в стандартный вывод.' ; //OutToFile PROCEDURE ?OutToFile STRING IN aValue %SUMMARY ' Выводит значение в текущий файл, если оно не пустое. С переводом каретки. ' ; // %SUMMARY if ( aValue =/= '' ) then ( aValue OutToFile ) ; // ?OutToFile FUNCTOR TYPE GENERATOR %REMARK 'Генератор содержимого элемента' GENERATOR VAR g_CurrentGenerator %REMARK 'Текущий генератор' ( g_CurrentGenerator := nil ) STRING VAR g_CurrentGeneratorName %REMARK 'Имя текущего генератора' ( g_CurrentGeneratorName := '' ) CONST cPathSep '\' STRING FUNCTION OutDir sysutils:GetCurrentDir =: Result [ Result script:FileName %REMARK 'Путь к текущему скрипту' sysutils:ExtractFileName %REMARK 'Вырезаем из пути только имя файла' '' sysutils:ChangeFileExt %REMARK 'Убираем .script' '' sysutils:ChangeFileExt %REMARK 'Убираем .ms' ] cPathSep strings:CatSep =: Result ; // OutDir STRING FUNCTION OutFileName STRING right aGeneratorName %SUMMARY 'Имя файла для вывода' ; STRING VAR l_OutPath %REMARK 'Путь для вывода' l_OutPath := OutDir l_OutPath sysutils:ForceDirectories ?ASSURE [ 'Не удалось создать директорию ' l_OutPath ] %REMARK 'Создаём директорию рекурсивно, если её ещё не было' [ l_OutPath aGeneratorName ] cPathSep strings:CatSep =: Result ; // OutFileName BOOLEAN elem_func IsStereotypeOf LINK RIGHT IN aStereo %SUMMARY 'Определяет, что элемент является указанным стереотипом или его потомком' ; Result := ( Self .Stereotype .Name = ( aStereo .Name ) ) ; // IsStereotypeOf USES CommonLang.ms.model ; // - это - "времянка", чтобы увидеть символ <<Const>> BOOLEAN elem_func IsElementNeed.OwnFile %SUMMARY 'Определяет тот факт, что элементу нужен собственный файл вывода' ; Result := true if ( Self .IsStereotypeOf <<Const>> ) then begin Result := false end ; // IsElementNeed.OwnFile ModelElement VAR g_CurrentElementModel %REMARK 'Текущий генерируемый элемент' ( g_CurrentElementModel := nil ) STRING VAR g_OutFileName ( g_OutFileName := '' ) elem_proc CallCurrentGen FILE VAR l_PrevOutFile l_PrevOutFile := g_OutFile INTEGER VAR l_PrevIndent l_PrevIndent := g_Indent TRY TRY STRING VAR l_PrevOutFileName l_PrevOutFileName := g_OutFileName TRY if ( Self .IsElementNeed.OwnFile ) then begin g_Indent := 0 // - тут вообще ещё надо проверять - переоткрываем мы файл или нет STRING VAR l_FileName g_OutFileName := ( g_OutFileName ?(+) '_' (+) ( Self .Name ) ) l_FileName := ( OutFileName g_OutFileName (+) '.' (+) g_CurrentGeneratorName ) g_OutFile := ( l_FileName File:OpenWrite ) end // Self .IsElementNeed.OwnFile TRY g_CurrentGenerator IsNil ! ?ASSURE 'Текущй генератор пустой' ModelElement VAR l_CurrentElementModel l_CurrentElementModel := g_CurrentElementModel TRY g_CurrentElementModel := Self Self ( g_CurrentGenerator DO ) %REMARK 'Вызываем на элементе генератор g_CurrentGenerator' FINALLY g_CurrentElementModel := l_CurrentElementModel END FINALLY g_OutFile := nil END // TRY..FINALLY FINALLY g_OutFileName := l_PrevOutFileName END FINALLY g_OutFile := l_PrevOutFile l_PrevOutFile := nil END FINALLY g_Indent := l_PrevIndent 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 'Вызывает текущий генератор для всех детей элемента модели' ; Self .Children .for> .Child.CallCurrentGen ; // CallChildrenCurrentGen WordAlias .generate.children .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 FieldByNameDeepest.ms.dict ; 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 ARRAY VAR g_AllRoots %REMARK 'Все корневые элементы' ( g_AllRoots := nil ) ARRAY VAR g_AllGenerators %REMARK 'Все генераторы' ( g_AllGenerators := nil ) PROCEDURE CallGens ARRAY IN anElements ARRAY IN aGenerators %SUMMARY 'Вызывает все определённые генераторы на элементах массива anElements' ; OutDir PureDir %REMARK 'Очищаем директорию от "старых" файлов' g_AllRoots := anElements TRY g_AllGenerators := aGenerators TRY for anElements ( ModelElement IN anElement for aGenerators ( GENERATOR IN aGen anElement .CallGen aGen %REMARK 'Вызываем на элементе anElement генератор aGen' ) // for aGenerators ) // for anElements FINALLY g_AllGenerators := nil END FINALLY g_AllRoots := nil END ; // CallGens PROCEDURE CallGensList Sequence LVALUE anElements Sequence LVALUE aGenerators ( anElements CodeIterator ) ( aGenerators CodeIterator ) CallGens ; // CallGensList WordAlias generate CallGensListСтоит обратить внимание на IsElementNeed.OwnFile и IsStereotypeOf.
Комментариев нет:
Отправить комментарий