UNIT Generation.ms.dict USES axiom_push.ms.dict ; USES core.ms.dict ; USES WordsRTTI.ms.dict ; USES ElementsRTTI.ms.dict ; USES CompileTimeVar.ms.dict ; USES SaveVarAndDo.ms.dict ; 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 |N ) 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 |N ) Ctx:Parser:PushSymbol ; // %GP CONST cPathSep '\' FILE CompileTime-VAR g_OutFile nil %REMARK 'Текущий файл' INTEGER CompileTime-VAR g_Indent 0 %REMARK 'Текущий отступ' CONST cIndentChar ' ' STRING FUNCTION IndentStr g_Indent cIndentChar char:Dupe >>> Result ; // IndentStr OBJECT STRING INTEGER ARRAY BOOLEAN TYPE OUTABLE STRING FUNCTION ValueToString OUTABLE IN aValue if ( aValue IsArray ) then begin [ aValue .for> call.me ] strings:Cat >>> Result end else if ( aValue .IsWord ) then begin aValue |N >>> Result end else begin aValue ToPrintable >>> Result end ; // ValueToString STRING FUNCTION ValueToStringOrName OUTABLE IN aValue if ( aValue .IsWord ) then begin aValue .Name >>> Result if ( Result = '' ) then begin aValue pop:Word:Name >>> Result end end else begin aValue ValueToString >>> Result end ; // ValueToStringOrName CONST \n #13#10 PROCEDURE .Out OUTABLE IN aValue [ IndentStr aValue ValueToString ] strings:Cat g_OutFile File:WriteStr \n g_OutFile File:WriteStr ; // .Out PROCEDURE Indented: ^ IN aLambda TF g_Indent ( INC g_Indent aLambda DO ) ; // Indented: PROCEDURE Bracketed ^ IN aLambda '{' .Out Indented: ( aLambda DO ) '}' .Out ; // Bracketed USES axiom:SysUtils ; USES arrays.ms.dict ; TtfwWord FUNCTION .FindMemberRecur STRING IN aName TtfwWord IN aGen TtfwKeyWord VAR l_Member aName aGen pop:Word:FindMember >>> l_Member if ( l_Member IsNil ) then ( nil >>> Result ) else ( l_Member pop:KeyWord:Word >>> Result ) if ( Result IsNil ) then ( aGen .Inherited.Words .for> ( IN anItem TtfwWord VAR l_Found aName anItem call.me >>> l_Found ( Result IsNil ) OR ( l_Found IsNil ) OR ( Result = l_Found ) ?ASSURE [ 'Multiply inheritance. Word: ' aName ' generator ' aGen pop:Word:Name ' parent generator ' anItem pop:Word:Name ] l_Found >>> Result ) ) ; // .FindMemberRecur ARRAY CompileTime-VAR g_GeneratedFiles [] %REMARK 'Ранее сгенерированные файлы' TtfwWord VAR g_CurrentGenerator %REMARK 'Текущий генератор' WordAlias Cached: CacheMethod WordAlias GenCached: CacheMethod : .? ^ IN aWord TtfwWord VAR l_Word aWord |N g_CurrentGenerator .FindMemberRecur >>> l_Word if ( l_Word IsNil ) then ( aWord DO ) else ( l_Word DO ) ; // .? STRING FUNCTION Ext '.dump' >>> Result ; // Ext PROCEDURE .GenerateWordToFile ModelElement IN Self ^ IN aLambda TF g_Indent ( 0 >>> g_Indent STRING VAR l_FileName [ Self pop:Word:Name .? Ext ] strings:Cat >>> l_FileName STRING VAR l_TempPath 'C:\Temp\GenScripts\' >>> l_TempPath l_TempPath sysutils:ForceDirectories ?ASSURE [ 'Не удалось создать директорию ' l_TempPath ] STRING VAR l_RealPath 'W:\common\GenScripts\' >>> l_RealPath l_RealPath sysutils:ForceDirectories ?ASSURE [ 'Не удалось создать директорию ' l_RealPath ] STRING VAR l_TempFileName [ l_TempPath l_FileName ] cPathSep strings:CatSep >>> l_TempFileName STRING VAR l_RealFileName [ l_RealPath l_FileName ] cPathSep strings:CatSep >>> l_RealFileName if ( g_GeneratedFiles l_TempFileName array:HasText ! ) then begin l_TempFileName array:AddTo g_GeneratedFiles TF g_OutFile ( l_TempFileName File:OpenWrite >>> g_OutFile Self aLambda DO ) if ( ( l_RealFileName sysutils:FileExists ! ) OR ( '' l_RealFileName l_TempFileName CompareFiles ! ) ) then begin $20 l_RealFileName l_TempFileName CopyFile end end // g_GeneratedFiles l_TempFileName array:HasText ! ) ; // .GenerateWordToFile PROCEDURE .DeleteWordFile ModelElement IN Self STRING VAR l_FileName [ Self pop:Word:Name .? Ext ] strings:Cat >>> l_FileName STRING VAR l_RealPath 'W:\common\GenScripts\' >>> l_RealPath STRING VAR l_RealFileName [ l_RealPath l_FileName ] cPathSep strings:CatSep >>> l_RealFileName if ( l_RealFileName sysutils:FileExists ) then begin l_RealFileName DeleteFile DROP end ; // .DeleteWordFile BOOLEAN elem_func IsScriptKeyword Self .IsStereotype st_ScriptKeyword >>> Result ; // IsScriptKeyword BOOLEAN elem_func IsSimpleClass Cached: ( RULES ( Self .IsStereotype st_SimpleClass ) true ( Self .IsStereotype st_Service ) true ( Self .IsStereotype st_ServiceImplementation ) true ( Self .IsScriptKeyword ) true ( Self .IsStereotype st_TestCase ) true ( Self .IsStereotype st_GuiControl ) true ( Self .IsStereotype st_VCMForm ) true ( Self .IsStereotype st_VCMFinalForm ) true ( Self .IsStereotype st_VCMContainer ) true ( Self .IsStereotype st_VCMFinalContainer ) true DEFAULT false ; // RULES ) >>> Result ; // IsSimpleClass BOOLEAN elem_func IsUtilityPack Cached: ( RULES ( Self .IsStereotype st_UtilityPack ) true ( Self .IsStereotype st_ScriptKeywordsPack ) true DEFAULT false ; // RULES ) >>> Result ; // IsUtilityPack BOOLEAN elem_func IsInterfaces Cached: ( RULES ( Self .IsStereotype st_Interfaces ) true ( Self .IsStereotype st_InternalInterfaces ) true DEFAULT false ; // RULES ) >>> Result ; // IsInterfaces BOOLEAN elem_func IsMixIn Self .IsStereotype st_Impurity >>> Result ; // IsMixIn BOOLEAN elem_func IsPureMixIn Self .IsStereotype st_PureMixIn >>> Result ; // IsPureMixIn BOOLEAN elem_func IsTypedef Self .IsStereotype st_Typedef >>> Result ; // IsTypedef BOOLEAN elem_func IsTagTable Self .IsStereotype st_TagTable >>> Result ; // IsTagTable BOOLEAN elem_func IsTarget Cached: ( RULES ( Self .IsStereotype st_ExeTarget ) true ( Self .IsStereotype st_AdapterTarget ) true ( Self .IsStereotype st_TestTarget ) true DEFAULT false ; // RULES ) >>> Result ; // IsTarget BOOLEAN FUNCTION NeedOwnFile ModelElement IN Self Cached: ( RULES ( Self .IsStereotype st_ScriptKeywords ) false ( Self .IsStereotype st_TestClass ) true ( Self .IsStereotype st_Atom ) true ( Self .IsStereotype st_Tag ) true ( Self .IsTarget ) true ( Self .IsStereotype st_TestResults ) true ( Self .IsTagTable ) true ( Self .IsInterfaces ) true ( Self .IsUtilityPack ) true ( Self .IsMixIn ) true ( Self .IsSimpleClass ) begin RULES ( Self .Visibility = ProtectedAccess ) false ( Self .Visibility = PrivateAccess ) false DEFAULT ( ModelElement VAR l_Parent Self .Parent >>> l_Parent if ( ( l_Parent .IsSimpleClass ) OR ( l_Parent .IsMixIn ) OR ( l_Parent .IsUtilityPack ) OR ( l_Parent .IsInterfaces ) ) then begin false end else begin true end ) ; // RULES end DEFAULT false ; // RULES ) >>> Result ; // NeedOwnFile PROCEDURE .CurrentGenerator ModelElement IN Self Self g_CurrentGenerator DO ; // .CurrentGenerator USES CallInherited.ms.dict ; USES classRelations.ms.dict ; BOOLEAN elem_func NeedOwnFile Self .? NeedOwnFile >>> Result ; // NeedOwnFile elem_proc dump Self .Out Bracketed ( Self MembersIterator .for> ( OBJECT IN aCode STRING VAR l_Out STRING VAR l_Name aCode pop:Word:Name >>> l_Name [ l_Name ' : ' ] strings:Cat >>> l_Out [ aCode DO ] .for> ( IN anItem if ( anItem .IsSequence ) then ( anItem .SequenceCode.It >>> anItem ) if ( anItem IsArray ) then begin if ( ( l_Name = 'Children' ) ) then begin '' >>> l_Out l_Name .Out Bracketed ( ARRAY VAR l_Items anItem .filter> ( .NeedOwnFile ! ) >>> l_Items l_Items .filter> ( .Visibility = PrivateAccess ) .for> call.me l_Items .filter> ( .Visibility = ProtectedAccess ) .for> call.me l_Items .filter> ( .Visibility = PublicAccess ) .for> call.me l_Items .filter> ( .Visibility = UnknownAccess ) .for> call.me ) // Bracketed end else if ( ( l_Name = 'Attributes' ) OR ( l_Name = 'Operations' ) OR ( l_Name = 'Constants' ) OR ( l_Name = 'Dependencies' ) OR ( l_Name = 'Parameters' ) ) then begin '' >>> l_Out l_Name .Out Bracketed ( ARRAY VAR l_Items anItem // .filter> ( .NeedOwnFile ! ) >>> l_Items l_Items .filter> ( .Visibility = PrivateAccess ) .for> call.me l_Items .filter> ( .Visibility = ProtectedAccess ) .for> call.me l_Items .filter> ( .Visibility = PublicAccess ) .for> call.me l_Items .filter> ( .Visibility = UnknownAccess ) .for> call.me ) // Bracketed end else begin l_Out [ anItem .for> ValueToStringOrName ] ' ' strings:CatSep Cat >>> l_Out end end // anItem IsArray else begin l_Out anItem ValueToStringOrName Cat >>> l_Out end // anItem IsArray if ( l_Out <> '' ) then begin l_Out .Out end // l_Out <> '' ) // [ aCode DO ] .for> ) // Self MembersIterator ) // Bracketed ; // dump PROCEDURE OutLn '' .Out ; // OutLn elem_proc WithDelim STRING IN aDelim TtfwWord IN aVar TtfwWord IN aLambda [ if ( aVar DO ! ) then begin true aVar pop:Word:SetValue end else begin aDelim end Self ] aLambda DO ; // WithDelim elem_proc WithComma: ^ IN aVar ^ IN aLambda Self ', ' aVar aLambda .WithDelim ; // WithComma: STRING FUNCTION .CutT STRING IN aName aName >>> Result if ( 'T' Result StartsStr ) then begin Result 'T' '' string:ReplaceFirst >>> Result end // 'T' Result StartsStr ; // .CutT STRING elem_func UnitName GenCached: ( STRING VAR l_Path Self .GetUP 'intf.pas:Path' >>> l_Path RULES ( l_Path <> '' ) ( 'w:\' // - это потому, что в пути нету диска, а для ExtractFileName он нужен l_Path Cat sysutils:ExtractFileName '' sysutils:ChangeFileExt 'NOT_FINISHED_' '' string:ReplaceFirst ) ( Self IsNil ) '' ( Self .IsTagTable ) ( Self .Name '_Schema' Cat ) ( Self .IsScriptKeyword ) ( Self .Name .CutT ) ( Self .IsSimpleClass ) ( Self .Name .CutT ) DEFAULT ( Self .Name ) ; // RULES ) >>> Result ; // UnitName ModelElement elem_func UnitProducer GenCached: ( RULES ( Self IsNil ) nil ( Self IsString ) Self ( Self .NeedOwnFile ) Self DEFAULT ( Self .Parent call.me ) ; // RULES ) >>> Result ; // UnitProducer ARRAY FUNCTION .filterNil> ARRAY IN anArray anArray .filter> ( IsNil ! ) >>> Result ; // .filterNil> ARRAY FUNCTION .filterMixIns> ARRAY IN anArray anArray .filter> ( .IsMixIn ! ) // .filter> ( .IsPureMixIn ! ) >>> Result ; // .filterMixIns> elem_proc OutUses: ^ IN aUsed ^ IN aLambda ARRAY VAR l_Used aUsed DO >>> l_Used ARRAY FUNCTION .filterUsed> ARRAY IN anArray anArray .filter> ( IN anItem if ( anItem l_Used array:Has ! ) then begin anItem array:AddTo l_Used true end else begin false end ) >>> Result ; // .filterUsed> 'uses' .Out BOOLEAN VAR l_NeedComma false >>> l_NeedComma Indented: ( aLambda DO .map> .UnitProducer .filterNil> .filterMixIns> .filter> ( Self ?!= ) .filter> ( .UnitName Self .UnitName ?!= ) .filter> ( .UnitName 'System' ?!= ) .map> .UnitName .filterUsed> .for> ( .WithComma: l_NeedComma .Out ) ) // Indented: ';' .Out OutLn ; // OutUses: ARRAY FUNCTION .mapToTarget> ARRAY IN anArray anArray .map> .Target >>> Result ; // .mapToTarget> ARRAY FUNCTION .joinWithLambded> ARRAY IN anArrayToJoin ^ IN anArrayToIterate ^ IN aLambda anArrayToJoin anArrayToIterate DO .for> ( IN aChild .join> ( aChild aLambda DO ) ) >>> Result ; // .joinWithLambded> ARRAY FUNCTION .mapToTargetAndValueType> ARRAY IN anArray anArray .mapToTarget> .join> ( anArray .map> .ValueType ) >>> Result ; // .mapToTargetAndValueType> elem_iterator AttributesAndOperations Cached: ( Self .Attributes .join> ( Self .Operations ) ) >>> Result ; // AttributesAndOperations elem_iterator ChildrenWithoutOwnFile Cached: ( Self .Children .filter> ( .NeedOwnFile ! ) ) >>> Result ; // ChildrenWithoutOwnFile elem_iterator ConstantsAndChildrenWithoutOwnFile Cached: ( Self .Constants .join> ( Self .ChildrenWithoutOwnFile ) ) >>> Result ; // ConstantsAndChildrenWithoutOwnFile elem_iterator AllOwnChildren Cached: ( Self .ConstantsAndChildrenWithoutOwnFile .join> ( Self .AttributesAndOperations ) ) >>> Result ; // AllOwnChildren ARRAY FUNCTION .OperationsNeededElements ARRAY IN anArray anArray .mapToTargetAndValueType> .joinWithLambded> anArray ( .Parameters .mapToTargetAndValueType> ) .joinWithLambded> anArray ( .AttributesAndOperations call.me ) >>> Result ; // .OperationsNeededElements elem_iterator NeededElements ( Self .Inherits ) .join> ( Self .Implements ) .join> ( Self .AttributesAndOperations .OperationsNeededElements ) if ( Self .IsTypedef ! ) then begin .join> ( Self .Implemented .OperationsNeededElements ) .join> ( Self .Overridden .OperationsNeededElements ) end // Self .IsTypedef ! >>> Result ; // NeededElements elem_iterator NeededElementsTotal Self .NeededElements .joinWithLambded> ( Self .ConstantsAndChildrenWithoutOwnFile ) call.me >>> Result ; // NeededElementsTotal BOOLEAN elem_func IsForInterface Cached: ( Self .Visibility PublicAccess == ) >>> Result ; // IsForInterface BOOLEAN elem_func IsForImplementation Cached: ( Self .IsForInterface ! ) >>> Result ; // IsForImplementation elem_iterator IntfUses [ 'l3IntfUses' ] if ( Self .IsForInterface ) then begin .join> ( Self .NeededElementsTotal ) end // Self .IsForInterface >>> Result ; // IntfUses BOOLEAN elem_func IsInterface Self .IsStereotype st_Facet >>> Result ; // IsInterface elem_iterator InjectedElements Self .Injected .filter> ( .IsStereotype st_injects::Dependency ) .map> .Parent >>> Result ; // InjectedElements BOOLEAN elem_func IsClassImplementable Cached: ( RULES ( Self .IsPureMixIn ) false ( Self .IsMixIn ) false ( Self .IsStereotype st_Atom ) false DEFAULT true ; // RULES ) >>> Result ; // IsClassImplementable elem_iterator Used Cached: ( Self .Dependencies .filter> ( .IsStereotype st_uses::Dependency ) .mapToTarget> if ( Self .IsInterface ! ) then begin .join> ( Self .InjectedElements ) end // Self .IsInterface ! ) >>> Result ; // Used elem_iterator UsedTotal Self .Used .joinWithLambded> ( Self .AllOwnChildren ) call.me >>> Result ; // UsedTotal elem_iterator ImplUses [ 'l3ImplUses' ] if ( Self .IsForImplementation ) then begin .join> ( Self .NeededElementsTotal ) end // Self .IsForImplementation .join> ( Self .UsedTotal ) >>> Result ; // ImplUses STRING elem_func TypeName Cached: ( STRING VAR l_ExtName Self .GetUP 'extprop:pas:TypeName' >>> l_ExtName RULES ( l_ExtName <> '' ) l_ExtName DEFAULT ( Self .Name ) ; // RULES ) >>> Result ; // TypeName BOOLEAN elem_func IsClass Self .IsSimpleClass >>> Result ; // IsClass ModelElement elem_func MainAncestor Cached: ( ModelElement VAR l_Found nil >>> l_Found Self .Inherits .trunc> ( DROP l_Found IsNil ) .for> ( >>> l_Found ) l_Found ) >>> Result ; // MainAncestor : .With() OUTABLE IN aValue if ( aValue IsNil ! ) then [ '(' aValue ')' ] ; // .With() elem_proc OutDocumentation STRING VAR l_Doc Self .Documentation >>> l_Doc if ( l_Doc IsNil ! ) then begin l_Doc '{' '[' string:Replace >>> l_Doc l_Doc '}' ']' string:Replace >>> l_Doc Indented: ( [ '{* ' l_Doc ' }' ] .Out ) end // l_Doc IsNil ! ; // OutDocumentation elem_proc OutClass [ Self .TypeName ' = class' [ Self .MainAncestor ] .join> ( Self .Implements .filter> .IsClassImplementable ) .map> .TypeName ', ' strings:CatSep .With() ] .Out Self .OutDocumentation [ 'end;//' Self .TypeName ] .Out ; // OutClass elem_proc OutInterface [ Self .TypeName ' = interface' Self .MainAncestor .TypeName .With() ] .Out [ 'end;//' Self .TypeName ] .Out ; // OutInterface elem_proc OutType RULES ( Self .IsClass ) ( Self .OutClass ) ( Self .IsInterface ) ( Self .OutInterface ) DEFAULT ( [ '// ' Self .TypeName ] .Out ) ; // RULES OutLn ; // OutType BOOLEAN elem_func IsType Cached: ( RULES ( Self .IsUtilityPack ) false ( Self .IsInterfaces ) false ( Self .IsTarget ) false DEFAULT true ; // RULES ) >>> Result ; // IsType elem_proc OutTypes ^ IN aValid BOOLEAN VAR l_WasType false >>> l_WasType elem_proc DoOutTypes Self .ChildrenWithoutOwnFile .for> call.me if ( Self aValid DO ) then begin if ( Self .IsType ) then begin if ( l_WasType ! ) then begin 'type' .Out true >>> l_WasType end Indented: ( Self .OutType ) end // Self .IsType end ; // DoOutTypes Self .DoOutTypes ; // OutTypes elem_proc OutInterfaceSection Self .OutTypes .IsForInterface ; // OutInterfaceSection elem_proc OutImplementationSection Self .OutTypes .IsForImplementation ; // OutImplementationSection elem_proc OutUnit [ 'unit ' Self .UnitName ';' ] .Out OutLn 'interface' .Out OutLn ARRAY VAR l_Used [] >>> l_Used Self .OutUses: l_Used ( Self .IntfUses ) Self .OutInterfaceSection 'implementation' .Out OutLn Self .OutUses: l_Used ( Self .ImplUses ) Self .OutImplementationSection 'end.' .Out ; // OutUnit elem_proc OutMixIn Self .Name .Out ; // OutMixIn elem_generator pas CONST Ext '.pas' RULES ( Self .IsMixIn ) ( Self .OutMixIn ) ( Self .IsInterfaces ) ( Self .OutUnit ) ( Self .IsSimpleClass ) ( Self .OutUnit ) ( Self .IsUtilityPack ) ( Self .OutUnit ) ( Self .IsStereotype st_TestClass ) ( Self .OutUnit ) ( Self .IsTarget ) ( Self .OutUnit ) ( Self .IsTagTable ) ( Self .OutUnit ) DEFAULT ( Self .dump ) ; // RULES ; // pas elem_generator res.cmd Inherits .pas CONST Ext '.res.cmd' BOOLEAN FUNCTION NeedOwnFile ModelElement IN Self Self .UPisTrue "needs script" >>> Result ; // NeedOwnFile [ 'MakeCo ' Self .Name '.rc.script' ] .Out [ 'brcc32 ' Self .Name '.rc' ] .Out //call.inherited ; // res.cmd elem_generator rc Inherits .res.cmd CONST Ext '.rc' [ Self .Name ' RCDATA LOADONCALL MOVEABLE DISCARDABLE ' Self .Name '.rc.script.co' ] .Out //call.inherited ; // rc ARRAY CompileTime-VAR g_GeneratedElements [] %REMARK 'Ранее сгенерированные элементы' PROCEDURE .GenerateWithChildren ModelElement IN Self Sequence IN aGenerators if ( Self g_GeneratedElements array:Has ! ) then begin Self array:AddTo g_GeneratedElements aGenerators CodeIterator .for> ( // - цикл по генераторам для Self TtfwWord IN aGenerator TF g_CurrentGenerator ( aGenerator >>> g_CurrentGenerator if ( Self .NeedOwnFile ) then ( Self .GenerateWordToFile .CurrentGenerator ) else ( Self .DeleteWordFile ) ) // TF g_CurrentGenerator ) // aGenerators CodeIterator .for> Self .Children // .filter> ( .NeedOwnFile ) .for> ( aGenerators call.me ) // - тут генерируем детей end // Self g_GeneratedElements array:Has ! ; // .GenerateWithChildren PROCEDURE .call.generators.in.list ModelElement IN Self Sequence ^ IN aGenerators Self aGenerators .GenerateWithChildren ; // .call.generators.in.list PROCEDURE .Generate ModelElement IN Self g_GeneratedFiles = nil ?FAIL 'Массив g_GeneratedFiles не инициализирован' g_GeneratedElements = nil ?FAIL 'Массив g_GeneratedElements не инициализирован' Self .call.generators.in.list ( .pas .res.cmd .rc ) ; // .Generate
Функциональщина....
.filter>, .map> и .join> меня особенно "заводят".
Они позволяют работать с коллекциями элементов без их копирования.
Ну и Cached: - тоже весело - позволяет вызывать функцию только один раз, а потом обращаться к её кешированному значению.
Жаль, нет присутствовавшей в следующих постах ссылки на предысторию. Хотел было докопаться до сути серии постов, но тут вот упёрся в отсутствие ссылки )
ОтветитьУдалить