unit dpglib.GrammarBehavior; interface uses System.Classes, dpgrtl.types, dpglib.Types; type TGrammarBehavior = class( TInterfacedObject, IGrammarBehavior) protected fGrammar : IGrammar; fTool : ITool; fAnalyzer : ILLkAnalyzer; fUsesList : TStringList; fIsLexer : boolean; fIsParser : boolean; fIsTreeWalker : boolean; fLexerGrammar : ILexerGrammar; fParserGrammar : IParserGrammar; fTreeWalkerGrammar: ITreeWalkerGrammar; fLanguage : AnsiString; fExchangeDir : AnsiString; fConstAction : IToken; fTypeAction : IToken; protected // ------------------------------------------------------------ // _RefRule // ------------------------------------------------------------ procedure _RefStringLiteral( pLiteral : IToken); procedure _RefToken( pToken : IToken); procedure _RefRule( pRuleName : IToken); public constructor Create( pTool : ITool; pAnalyzer : ILLkAnalyzer; pExchangeDir : AnsiString); destructor Destroy; override; public // ------------------------------------------------------------ // IGrammarBehavior methods // ------------------------------------------------------------ function Grammar: IGrammar; procedure AbortGrammar; virtual; procedure BeginAlt( pDoAST: boolean); virtual; abstract; procedure BeginExceptionGroup; virtual; abstract; procedure BeginExceptionSpec( pLabel : IToken); virtual; abstract; procedure BeginSubRule( pLabel : IToken; pStart : IToken; pNot : boolean); virtual; abstract; procedure BeginTree( pStart : IToken); virtual; abstract; procedure BeginChildList; virtual; abstract; procedure DefineGrammarUnit( pUnit : AnsiString); virtual; procedure DefineRuleName( pRule : IToken; pAccess : AnsiString; pRuleAutoGen : boolean; pDocComment : AnsiString); virtual; procedure DefineToken( pTokenName : IToken; pTokenLiteral : IToken); virtual; procedure DefineUses( pUses : AnsiString); virtual; procedure EndAlt; virtual; abstract; procedure EndExceptionGroup; virtual; abstract; procedure EndExceptionSpec; virtual; abstract; procedure EndGrammar; virtual; abstract; procedure EndOptions; virtual; procedure EndRule( pRuleName : AnsiString); virtual; abstract; procedure EndSubRule; virtual; abstract; procedure EndTree; virtual; abstract; procedure EndChildList; virtual; abstract; procedure HasError; virtual; abstract; procedure NoASTSubRule; virtual; abstract; procedure OneOrMoreSubRule; virtual; abstract; procedure NMSubRule; virtual; abstract; procedure OptionalSubRule; virtual; abstract; procedure refRangeLow( M : integer); virtual; abstract; procedure refRangeHigh( N : integer); virtual; abstract; procedure RefAction( pAction : IToken); virtual; abstract; procedure RefArgAction( pAction : IToken); virtual; abstract; procedure RefCharLiteral( pLiteral : IToken; pLabel : IToken; pInverted : boolean; pAutoGenType : integer; pLastInRule : boolean); virtual; abstract; procedure RefCharRange( pToken1 : IToken; pToken2 : IToken; pLabel : IToken; pAutoGenType : integer; pLastInRule : boolean); virtual; abstract; procedure RefConstAction( pConstAction : IToken); virtual; procedure RefTypeAction( pTypeAction : IToken); virtual; procedure RefElemOption( pOption : IToken; pValue : IToken); virtual; abstract; procedure RefTokenSpecElemOption( pToken : IToken; pOption : IToken; pValue : IToken); virtual; abstract; procedure RefExceptionHandler( pTypeAndName : IToken; pAction : IToken); virtual; abstract; procedure RefInitAction( pAction : IToken); virtual; abstract; procedure RefMemberDecl( pDecl : IToken); virtual; procedure RefMemberDef( pDef : IToken); virtual; procedure RefReturnAction( pAction : IToken); virtual; abstract; procedure RefRule( pAssignId : IToken; pRuleName : IToken; pLabel : IToken; pArguments : IToken; pAutoGenType : integer); virtual; procedure RefRuleExHandler( pExHandlerType : IToken; pExHandlerCode : IToken); virtual; abstract; procedure RefAltExHandler( pExHandlerType : IToken; pExHandlerCode : IToken); virtual; abstract; procedure RefRuleLocals( pLocals : IToken); virtual; abstract; procedure RefSemPred( pSemPred : IToken); virtual; abstract; procedure RefStringLiteral( pLiteral : IToken; pLabel : IToken; pAutoGenType : integer; pLastInRule : boolean); virtual; procedure RefToken( pAssignId : IToken; pToken : IToken; pLabel : IToken; pArguments : IToken; pInverted : boolean; pAutoGenType : integer; pLAstInRule : boolean); virtual; procedure RefTokenRange( pToken1 : IToken; pToken2 : IToken; pLabel : IToken; pAutoGenType : integer; pLastInRule : boolean); virtual; procedure RefWildCard( pToken : IToken; pLabel : IToken; pAutoGenType : integer); virtual; abstract; procedure Reset; virtual; procedure SetArgOfRuleRef( pArguments : IToken); virtual; abstract; procedure SetCharVocabulary( pVocabulary : TByteSet);virtual; procedure setFileOption( poption : IToken; pValue : IToken; pFileName : AnsiString); virtual; procedure SetGrammarOption( pOption : IToken; pValue : IToken); virtual; procedure setRuleOption( pOption : IToken; pValue : IToken); virtual; abstract; procedure SetSubRuleOption( pOption : IToken; pValue : IToken); virtual; abstract; procedure SetUserExceptions( pException : AnsiString); virtual; abstract; procedure StartLexer( pFileName : AnsiString; pLexerName : IToken; pSuperClass : IToken); procedure StartParser( pFileName : AnsiString; pParserName : IToken; pSuperClass : IToken); procedure StartTreeWalker( pFileName : AnsiString; pParserName : IToken; pSuperClass : IToken); procedure SynPred; virtual; abstract; procedure ZeroOrMoreSubRule; virtual; abstract; end; implementation uses System.SysUtils, dpglib.Utils, dpglib.Messages, dpglib.DpgParserTokens, dpglib.TokenSymbol, dpglib.StringSymbol, dpglib.RuleSymbol, dpglib.LexerGrammar, dpglib.ParserGrammar, dpglib.TreeParserGrammar, dpglib.CodeGenerator; // **************************************************************************** // Constructor/destructor // **************************************************************************** // ---------------------------------------------------------------------------- // Constructor // ---------------------------------------------------------------------------- constructor TGrammarBehavior.Create(pTool : ITool; pAnalyzer : ILLkAnalyzer; pExchangeDir: AnsiString); begin inherited Create; fTool := pTool; fAnalyzer := pAnalyzer; fLexerGrammar := nil; fParserGrammar := nil; fTreeWalkerGrammar:= nil; fIsLexer := false; fIsParser := false; fIsTreeWalker := false; fConstAction := nil; fTypeAction := nil; fLanguage := 'Delphi'; fExchangeDir := pExchangeDir; if fExchangeDir <> '' then if fExchangeDir[Length(fExchangeDir)] <> '\' then fExchangeDir := fExchangeDir + '\'; end; // ---------------------------------------------------------------------------- // Destructor // ---------------------------------------------------------------------------- destructor TGrammarBehavior.Destroy; begin fTool := nil; fAnalyzer := nil; fLexerGrammar := nil; fParserGrammar := nil; fTreeWalkerGrammar:= nil; fIsLexer := false; fIsParser := false; fIsTreeWalker := false; inherited; end; // @@@: IGrammarBehavior implementation +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // IGrammarBehavior implementation // // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // ================================================================================================ // DefineGrammarUnit // ================================================================================================ procedure TGrammarBehavior.DefineGrammarUnit(pUnit: AnsiString); begin fGrammar.UnitName := pUnit; end; // ================================================================================================ // DefineRuleName // ================================================================================================ procedure TGrammarBehavior.DefineRuleName( pRule : IToken; pAccess : AnsiString; pRuleAutoGen: boolean; pDocComment : AnsiString); var id: AnsiString; ts: ITokenSymbol; rs: IRuleSymbol; begin id := pRule.TokenText; // --------------------------------------------------------------- // Handle lexer rule definition. Lexer rule name must be TOKENREF, // what means: must begin with capital letter. // --------------------------------------------------------------- if( pRule.TokenType = TT_TOKENREF) then begin // ------------------------------------------------------------ // construct valid name for it... // ------------------------------------------------------------ id := TCodeGenerator.encodeLexerRuleName( id); // ------------------------------------------------------------ // If this token not defined in TokenManager, define it. This // happens when the rule definition is before any reference to // this rule. // ------------------------------------------------------------ if not fGrammar.TokenManager.TokenDefined[ pRule.TokenText] then begin ts := TTokenSymbol.Create( pRule.TokenText); ts.TokenType:= fGrammar.TokenManager.NextTokenType; fGrammar.TokenManager.Define( ts); end; end; // --------------------------------------------------------------- // Check if the rule is already defined in the grammar. If so, // show an error message. In this case the generated code is not // usable.... // --------------------------------------------------------------- if fGrammar.Defined( id) then begin fGrammar.Symbol[id].QueryInterface(IRuleSymbol,rs); if rs.Defined then begin fTool.Error( Format( MSG_E_RULEREDEF,[ id]), fGrammar.GrammarFile, pRule.TokenLine, pRule.TokenColumn); end; end // --------------------------------------------------------------- // ... else the grammar isn't defined, so define it. // --------------------------------------------------------------- else begin rs := TRuleSymbol.Create( id); fGrammar.Define( rs); end; rs.Defined := true; rs.Access := pAccess; rs.Comment := pDocComment; end; // ================================================================================================ // DefineToken // // Define a token from tokens {...}. // Must be label and literal or just label or just a literal. // ================================================================================================ procedure TGrammarBehavior.DefineToken( pTokenName : IToken; pTokenLiteral : IToken); var i : integer; name : AnsiString; literal : AnsiString; ss : IStringSymbol; sx : IStringSymbol; ts : ITokenSymbol; begin name := ''; literal := ''; if pTokenName <> nil then name := pTokenName.TokenText; if pTokenLiteral <> nil then literal := pTokenLiteral.TokenText; // --------------------------------------------------------------- // The literal must be a string of printable characters surrounded // by '"'s. See: dpglib.utils. // --------------------------------------------------------------- if literal <> '' then begin for i:=2 to Length(literal) -1 do begin if not IsPrint( literal[i]) then begin fTool.Error( Format( MSG_E_INVSTRINGLITERAL,[ literal]), fGrammar.GrammarFile, pTokenLiteral.TokenLine, pTokenLiteral.TokenColumn); exit; end; end; end; // --------------------------------------------------------------- // OK the literal passed the test, so go... // --------------------------------------------------------------- if literal <> '' then begin ss := nil; ts := fGrammar.TokenManager.TokenSymbol[ literal]; if ts <> nil then ts.QueryInterface( IStringSymbol,ss); if ss <> nil then begin // --------------------------------------------------------- // This literal is known already. // If the literal has no label already, but we can provide // one here, then no problem, just map the label to the literal // and don't change anything else. // Otherwise, labels conflict: error. // --------------------------------------------------------- if (name = '') or (ss.Lbl <> '') then begin fTool.Warning( Format( MSG_W_TOKENSREDEF,[ literal]), fGrammar.GrammarFile, pTokenLiteral.TokenLine, pTokenLiteral.TokenColumn); exit; end // --------------------------------------------------------- // The literal had no label, but new def does. Set it. // --------------------------------------------------------- else if name <> '' then begin ss.Lbl := name; fGrammar.TokenManager.MapToTokenSymbol( name, ss); end; end; // ------------------------------------------------------------ // if they provide a name/label and that name/label already // exists, just hook this literal onto old token. // ------------------------------------------------------------ if name <> '' then begin ts := fGrammar.TokenManager.TokenSymbol[ name]; if ts <> nil then begin // ------------------------------------------------------ // Watch out that the label is not more than just a token. // If it already has a literal attached, then: conflict. // ------------------------------------------------------ if ts.QueryInterface( IStringSymbol, sx) = S_OK then begin fTool.Warning( Format( MSG_W_TOKENSREDEF,[ name]), fGrammar.GrammarFile, pTokenName.TokenLine, pTokenName.TokenColumn); exit; end; // ------------------------------------------------------ // A simple token symbol such as DECL is defined must // convert it to a AnsiStringLiteralSymbol with a label by // co-opting token type and killing old TokenSymbol. // Kill mapping and entry in vector of token manager. // ------------------------------------------------------ // ------------------------------------------------------ // Now create AnsiString literal with label. // ------------------------------------------------------ ss := TStringSymbol.Create( literal); ss.TokenType := ts.TokenType; ss.Lbl := name; // ------------------------------------------------------ // Redefine this critter as a AnsiString literal. // ------------------------------------------------------ fGrammar.TokenManager.Define( ss); // ------------------------------------------------------ // Make sure the label can be used also. // ------------------------------------------------------ fGrammar.TokenManager.MapToTokenSymbol( name, ss); exit; end; end; // ------------------------------------------------------------ // Here, literal was labeled but not by a known token symbol. // ------------------------------------------------------------ ss := TStringSymbol.Create( literal); ss.TokenType:= fGrammar.TokenManager.NextTokenType; ss.Lbl := name; fGrammar.TokenManager.Define(ss); if name <> '' then fGrammar.TokenManager.MapToTokenSymbol( name, ss); end // --------------------------------------------------------------- // Create a token in the token manager not a literal. // --------------------------------------------------------------- else begin if fGrammar.TokenManager.TokenDefined[ name] then begin fTool.Warning( Format( MSG_W_TOKENSREDEF,[ name]), fGrammar.GrammarFile, pTokenName.TokenLine, pTokenname.TokenColumn); exit; end; ts := TTokenSymbol.Create( name); ts.TokenType:= fGrammar.TokenManager.NextTokenType; fGrammar.TokenManager.Define(ts); end; end; // ================================================================================================ // DefineUses // ================================================================================================ procedure TGrammarBehavior.DefineUses(pUses: AnsiString); begin fGrammar.UsesList.Add( pUses); end; // ================================================================================================ // EndOptions // // Called after the optional options section, to compensate for options that // may not have been set. // This method is bigger than it needs to be, but is much more clear if I // delineate all the cases. // ================================================================================================ procedure TGrammarBehavior.EndOptions; begin { TODO : EndOptions not implemented... } end; // ================================================================================================ // refRule // ================================================================================================ procedure TGrammarBehavior.refRule( pAssignId : IToken; pRuleName : IToken; pLabel : IToken; pArguments : IToken; pAutoGenType: integer); begin _refRule( pRuleName); end; // ============================================================================ // ============================================================================ // RefStringLiteral // ============================================================================ procedure TGrammarBehavior.refStringLiteral( pLiteral : IToken; pLabel : IToken; pAutoGenType : integer; pLastInRule : boolean); begin _refStringLiteral( pLiteral); end; // ============================================================================ // ============================================================================ // RefToken // ============================================================================ procedure TGrammarBehavior.refToken( pAssignId : IToken; pToken : IToken; pLabel : IToken; pArguments : IToken; pInverted : boolean; pAutoGenType : integer; pLAstInRule : boolean); begin _refToken( pToken); end; // ============================================================================ // ============================================================================ // RefTokenRange // ============================================================================ procedure TGrammarBehavior.refTokenRange( pToken1 : IToken; pToken2 : IToken; pLabel : IToken; pAutoGenType : integer; pLastInRule : boolean); begin // --------------------------------------------------------------- // Ensure that the DefineGrammar methods are called; // otherwise a range addes more token refs to the alternative by // calling MakeGrammar.refToken etc... // --------------------------------------------------------------- if pToken1.TokenText[1] = '"' then _refStringLiteral( pToken1) else _RefToken( pToken1); if pToken2.TokenText[1] = '"' then _RefStringLiteral( pToken2) else _RefToken( pToken2); end; // ============================================================================ // ============================================================================ // SetCharVocabulary // // Set the character vocabulary for a lexer. // ============================================================================ procedure TGrammarBehavior.SetCharVocabulary(pVocabulary: TByteSet); begin if fIsLexer then fLexerGrammar.CharVocabulary := pVocabulary; end; // ============================================================================ // ============================================================================ // SetFileOption // ============================================================================ procedure TGrammarBehavior.SetFileOption( pOption : IToken; pValue : IToken; pFileName: AnsiString); begin // --------------------------------------------------------------- // Option: language // --------------------------------------------------------------- if pOption.TokenText = 'language' then if pValue.TokenType = TT_STRINGLIT then fLanguage := Copy(pValue.TokenText,2,Length(pValue.TokenText)-2) else if pValue.TokenType in [TT_TOKENREF,TT_RULEREF] then fLanguage := pValue.TokenText else fTool.Error( 'option "language" must be a AnsiString or identifier', fGrammar.GrammarFile, pValue.TokenLine, pValue.TokenColumn) // --------------------------------------------------------------- // Other option is error // --------------------------------------------------------------- else fTool.Error( 'Invalid file-level option: "' + poption.TokenText + '"', fGrammar.GrammarFile, pValue.TokenLine, pValue.TokenColumn) end; // ---------------------------------------------------------------------------- // SetGrammarOption // ---------------------------------------------------------------------------- procedure TGrammarBehavior.SetGrammarOption( pOption : IToken; pValue : IToken); begin // --------------------------------------------------------------- // Option: exportVocab // --------------------------------------------------------------- if pOption.TokenText = 'exportVocab' then if pValue.TokenType in [TT_TOKENREF,TT_RULEREF] then // fGrammar.ExportVocab := fExchangeDir + pValue.TokenText fGrammar.ExportVocab := pValue.TokenText else fTool.Error( 'option "exportVocab" must be an identifier', fGrammar.GrammarFile, pValue.TokenLine, pValue.TokenColumn) // --------------------------------------------------------------- // Option: importVocab // --------------------------------------------------------------- else if pOption.TokenText = 'importVocab' then if pValue.TokenType in [TT_TOKENREF,TT_RULEREF] then begin pValue.TokenText := fExchangeDir + pValue.TokenText; fGrammar.ImportVocab := pValue end else fTool.Error( 'option "importVocab" must be an identifier', fGrammar.GrammarFile, pValue.TokenLine, pValue.TokenColumn) // --------------------------------------------------------------- // Other options sent to the grammar // --------------------------------------------------------------- else fGrammar.SetOption( pOption, pValue); end; // ================================================================================================ // Start Lexer // ================================================================================================ procedure TGrammarBehavior.StartLexer( pFileName : AnsiString; pLexerName : IToken; pSuperClass : IToken); begin // --------------------------------------------------------------- // Create lexer, and initialize it // --------------------------------------------------------------- if fGrammar = nil then begin fLexerGrammar := TLexerGrammar.Create(pLexerName,fTool,pSuperClass); fGrammar := fLexerGrammar; fGrammar.LLkAnalyzer := fAnalyzer; fGrammar.GrammarFile := pFileName; fAnalyzer.Grammar := fGrammar; fIsLexer := true; // ------------------------------------------------------------ // Append uses list... // ------------------------------------------------------------ fGrammar.UsesList.AddStrings( fUsesList); fGrammar.ConstAction := fConstAction; fGrammar.TypeAction := fTypeAction; end else fTool.Panic('A grammar already started'); end; // ================================================================================================ // Start Parser // ================================================================================================ procedure TGrammarBehavior.StartParser( pFileName : AnsiString; pParserName : IToken; pSuperClass : IToken); begin // --------------------------------------------------------------- // Create parser, and initialize it // --------------------------------------------------------------- if fGrammar = nil then begin fParserGrammar := TParserGrammar.Create(pParserName,fTool,pSuperClass); fGrammar := fParserGrammar; fGrammar.LLkAnalyzer := fAnalyzer; fGrammar.GrammarFile := pFileName; fAnalyzer.Grammar := fGrammar; fIsParser := true; // ------------------------------------------------------------ // Append uses list... // ------------------------------------------------------------ fGrammar.UsesList.AddStrings( fUsesList); fGrammar.ConstAction := fConstAction; fGrammar.TypeAction := fTypeAction; end else fTool.Panic('A grammar already started'); end; // ================================================================================================ // Start TreeWalker // ================================================================================================ procedure TGrammarBehavior.StartTreeWalker( pFileName : AnsiString; pParserName : IToken; pSuperClass : IToken); begin // --------------------------------------------------------------- // Create parser, and initialize it // --------------------------------------------------------------- if fGrammar = nil then begin fTreeWalkerGrammar := TTreeParserGrammar.Create( pParserName,fTool,pSuperClass); fGrammar := fTreeWalkerGrammar; fGrammar.GrammarFile := pFileName; fGrammar.LLkAnalyzer := fAnalyzer; fAnalyzer.Grammar := fGrammar; fIsParser := true; // ------------------------------------------------------------ // Append uses list... // ------------------------------------------------------------ fGrammar.UsesList.AddStrings( fUsesList); fGrammar.ConstAction := fConstAction; fGrammar.TypeAction := fTypeAction; end else fTool.Panic('A grammar already started'); end; // ============================================================================ // ============================================================================ // RefMemberDecl // ============================================================================ procedure TGrammarBehavior.RefMemberDecl(pDecl: IToken); begin if pDecl <> nil then fGrammar.MemberDecl := pDecl.TokenText; end; // ============================================================================ // ============================================================================ // RefMemberDef // ============================================================================ procedure TGrammarBehavior.RefMemberDef(pDef: IToken); begin if pDef <> nil then fGrammar.MemberDef := pDef.TokenText; end; // ============================================================================ // ============================================================================ // Grammar // ============================================================================ function TGrammarBehavior.Grammar: IGrammar; begin result := fGrammar; end; // ============================================================================ // ============================================================================ // AbortGrammar // ============================================================================ procedure TGrammarBehavior.AbortGrammar; begin Reset; end; // ============================================================================ // ============================================================================ // Reset // ============================================================================ procedure TGrammarBehavior.Reset; begin fGrammar := nil; fLexerGrammar := nil; fParserGrammar := nil; fTreeWalkerGrammar:= nil; end; // **************************************************************************** // Internals // **************************************************************************** // ============================================================================ // ============================================================================ // _RefStringLiteral // // AnsiStringLiterals are treated like tokens except by the lexer. // ============================================================================ procedure TGrammarBehavior._RefStringLiteral( pLiteral: IToken); var ss : IStringSymbol; str: AnsiString; begin if not fIsLexer then begin str := pLiteral.TokenText; if fGrammar.TokenManager.TokenSymbol[str] = nil then begin // fTool.Warning( 'String literal "' + str + '" not defined.', // fGrammar.GrammarFile, // pLiteral.TokenLine, // pLiteral.TokenColumn); // ss := TStringSymbol.Create( str); // ss.TokenType:= fGrammar.TokenManager.NextTokenType; // fGrammar.TokenManager.Define( ss); end; end; end; // ============================================================================ // ============================================================================ // _RefToken // ============================================================================ procedure TGrammarBehavior._RefToken( pToken: IToken); var id: AnsiString; ts: ITokenSymbol; begin id := pToken.TokenText; if not fGrammar.TokenManager.TokenDefined[ id] then begin // ------------------------------------------------------------ // Defining new TOKENREF is only allowed in lexer.. // ------------------------------------------------------------ if fIsLexer then begin ts := TTokenSymbol.Create( id); ts.TokenType:= fGrammar.TokenManager.NextTokenType; fGrammar.TokenManager.Define( ts); end; end; end; // ============================================================================ // ============================================================================ // _refRule // ============================================================================ procedure TGrammarBehavior._RefRule( pRuleName: IToken); var id: AnsiString; begin id := pRuleName.TokenText; if pRuleName.TokenType = TT_TOKENREF then id := TCodeGenerator.encodeLexerRuleName( id); if not fGrammar.Defined( id) then fGrammar.Define( TRuleSymbol.Create( id)); end; // ============================================================================ // ============================================================================ // RefConstAction // ============================================================================ procedure TGrammarBehavior.RefConstAction(pConstAction: IToken); begin fConstAction := pConstAction; end; // ============================================================================ // ============================================================================ // RefTypeAction // ============================================================================ procedure TGrammarBehavior.RefTypeAction(pTypeAction: IToken); begin fTypeAction := pTypeAction; end; end.