{ *************************************************************************** * * * This source is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This code is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * A copy of the GNU General Public License is available on the World * * Wide Web at . You can also * * obtain it by writing to the Free Software Foundation, * * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * *************************************************************************** Author: Mattias Gaertner Abstract: TPascalReaderTool enhances TPascalParserTool. This tool provides a lot of useful functions to read the output of the TPascalParserTool. } unit PascalReaderTool; {$ifdef FPC}{$mode objfpc}{$endif}{$H+} interface {$I codetools.inc} uses {$IFDEF MEM_CHECK} MemCheck, {$ENDIF} Classes, SysUtils, FileProcs, CodeToolsStrConsts, CodeTree, CodeAtom, CustomCodeTool, PascalParserTool, KeywordFuncLists, BasicCodeTools, SourceChanger, LinkScanner, AVL_Tree; type { TPascalReaderTool } TPascalReaderTool = class(TPascalParserTool) protected CachedSourceName: string; public // comments function CleanPosIsInComment(CleanPos, CleanCodePosInFront: integer; var CommentStart, CommentEnd: integer): boolean; // general extraction function ExtractNode(ANode: TCodeTreeNode; Attr: TProcHeadAttributes): string; function ExtractCode(StartPos, EndPos: integer; Attr: TProcHeadAttributes): string; function ExtractBrackets(BracketStartPos: integer; Attr: TProcHeadAttributes): string; function ExtractIdentCharsFromStringConstant( StartPos, MinPos, MaxPos, MaxLen: integer): string; function ReadStringConstantValue(StartPos: integer): string; function GetNodeIdentifier(Node: TCodeTreeNode): PChar; // properties function ExtractPropType(PropNode: TCodeTreeNode; InUpperCase, EmptyIfIndexed: boolean): string; function MoveCursorToPropType(PropNode: TCodeTreeNode): boolean; function MoveCursorToPropName(PropNode: TCodeTreeNode): boolean; function ExtractPropName(PropNode: TCodeTreeNode; InUpperCase: boolean): string; function ExtractProperty(PropNode: TCodeTreeNode; Attr: TProcHeadAttributes): string; function GetPropertyNameIdentifier(PropNode: TCodeTreeNode): PChar; function GetPropertyTypeIdentifier(PropNode: TCodeTreeNode): PChar; function PositionInPropertyName(PropNode: TCodeTreeNode; CleanPos: integer): boolean; function PropertyIsDefault(PropertyNode: TCodeTreeNode): boolean; function PropertyNodeHasParamList(PropNode: TCodeTreeNode): boolean; function PropNodeIsTypeLess(PropNode: TCodeTreeNode): boolean; function PropertyHasSpecifier(PropNode: TCodeTreeNode; const s: string; ExceptionOnNotFound: boolean = true): boolean; // procs function ExtractProcName(ProcNode: TCodeTreeNode; Attr: TProcHeadAttributes): string; function ExtractProcHead(ProcNode: TCodeTreeNode; Attr: TProcHeadAttributes): string; function ExtractClassNameOfProcNode(ProcNode: TCodeTreeNode): string; function ProcNodeHasSpecifier(ProcNode: TCodeTreeNode; ProcSpec: TProcedureSpecifier): boolean; function GetProcNameIdentifier(ProcNode: TCodeTreeNode): PChar; function FindProcNode(StartNode: TCodeTreeNode; const AProcHead: string; Attr: TProcHeadAttributes): TCodeTreeNode; function FindCorrespondingProcNode(ProcNode: TCodeTreeNode; Attr: TProcHeadAttributes = [phpInUpperCase,phpWithoutClassName,phpWithVarModifiers] ): TCodeTreeNode; function FindCorrespondingProcParamNode(ProcParamNode: TCodeTreeNode; Attr: TProcHeadAttributes = [phpInUpperCase,phpWithoutClassName,phpWithVarModifiers] ): TCodeTreeNode; function FindProcBody(ProcNode: TCodeTreeNode): TCodeTreeNode; function ProcBodyIsEmpty(ProcNode: TCodeTreeNode): boolean; procedure MoveCursorToFirstProcSpecifier(ProcNode: TCodeTreeNode); function MoveCursorToProcSpecifier(ProcNode: TCodeTreeNode; ProcSpec: TProcedureSpecifier): boolean; procedure MoveCursorToProcName(ProcNode: TCodeTreeNode; SkipClassName: boolean); function PositionInProcName(ProcNode: TCodeTreeNode; SkipClassName: boolean; CleanPos: integer): boolean; function PositionInFuncResultName(ProcNode: TCodeTreeNode; CleanPos: integer): boolean; function ProcNodeHasParamList(ProcNode: TCodeTreeNode): boolean; function NodeIsInAMethod(Node: TCodeTreeNode): boolean; function NodeIsMethodBody(ProcNode: TCodeTreeNode): boolean; function NodeIsFunction(ProcNode: TCodeTreeNode): boolean; function NodeIsConstructor(ProcNode: TCodeTreeNode): boolean; function NodeIsDestructor(ProcNode: TCodeTreeNode): boolean; function NodeIsForwardProc(ProcNode: TCodeTreeNode): boolean; function NodeIsOperator(ProcNode: TCodeTreeNode): boolean; function NodeIsResultIdentifier(Node: TCodeTreeNode): boolean; function NodeIsResultType(Node: TCodeTreeNode): boolean; // classes function ExtractClassName(ClassNode: TCodeTreeNode; InUpperCase: boolean): string; function ExtractClassInheritance(ClassNode: TCodeTreeNode; Attr: TProcHeadAttributes): string; function FindClassNode(StartNode: TCodeTreeNode; const AClassName: string; IgnoreForwards, IgnoreNonForwards: boolean): TCodeTreeNode; function FindClassNodeBackwards(StartNode: TCodeTreeNode; const AClassName: string; IgnoreForwards, IgnoreNonForwards: boolean): TCodeTreeNode; function FindClassNode(CursorNode: TCodeTreeNode): TCodeTreeNode; function FindClassNodeForMethodBody(ProcNode: TCodeTreeNode; IgnoreForwards, IgnoreNonForwards: boolean): TCodeTreeNode; function FindClassOrInterfaceNode(CursorNode: TCodeTreeNode; FindClassOfMethod: boolean = false): TCodeTreeNode; function FindClassSection(ClassNode: TCodeTreeNode; NodeDesc: TCodeTreeNodeDesc): TCodeTreeNode; function FindLastClassSection(ClassNode: TCodeTreeNode; NodeDesc: TCodeTreeNodeDesc): TCodeTreeNode; function FindClassNodeInInterface(const AClassName: string; IgnoreForwards, IgnoreNonForwards, ErrorOnNotFound: boolean): TCodeTreeNode; function FindClassNodeInUnit(const AClassName: string; IgnoreForwards, IgnoreNonForwards, IgnoreImplementation, ErrorOnNotFound: boolean): TCodeTreeNode; function FindFirstIdentNodeInClass(ClassNode: TCodeTreeNode): TCodeTreeNode; function ClassSectionNodeStartsWithWord(ANode: TCodeTreeNode): boolean; function IsClassNode(Node: TCodeTreeNode): boolean; // class, not object function FindInheritanceNode(ClassNode: TCodeTreeNode): TCodeTreeNode; // records function ExtractRecordCaseType(RecordCaseNode: TCodeTreeNode): string; // variables, types function FindVarNode(StartNode: TCodeTreeNode; const UpperVarName: string): TCodeTreeNode; function FindTypeNodeOfDefinition( DefinitionNode: TCodeTreeNode): TCodeTreeNode; function NodeIsPartOfTypeDefinition(ANode: TCodeTreeNode): boolean; function ExtractDefinitionNodeType(DefinitionNode: TCodeTreeNode): string; function ExtractDefinitionName(DefinitionNode: TCodeTreeNode): string; function PositionInDefinitionName(DefinitionNode: TCodeTreeNode; CleanPos: integer): boolean; function MoveCursorToParameterSpecifier(DefinitionNode: TCodeTreeNode ): boolean; function FindEndOfWithVar(WithVarNode: TCodeTreeNode): integer; function NodeIsIdentifierInInterface(Node: TCodeTreeNode): boolean; function NodeCanHaveForwardType(TypeNode: TCodeTreeNode): boolean; function NodeIsForwardType(TypeNode: TCodeTreeNode): boolean; function FindForwardTypeNode(TypeNode: TCodeTreeNode; SearchFirst: boolean): TCodeTreeNode; function FindTypeOfForwardNode(TypeNode: TCodeTreeNode): TCodeTreeNode; // arrays function ExtractArrayRange(ArrayNode: TCodeTreeNode; Attr: TProcHeadAttributes): string; // sections function GetSourceName(DoBuildTree: boolean = true): string; function GetSourceType: TCodeTreeNodeDesc; function GetSourceNamePos(var NamePos: TAtomPosition): boolean; function PositionInSourceName(CleanPos: integer): boolean; function ExtractSourceName: string; // uses sections procedure MoveCursorToUsesStart(UsesNode: TCodeTreeNode); procedure MoveCursorToUsesEnd(UsesNode: TCodeTreeNode); procedure ReadNextUsedUnit(out UnitNameAtom, InAtom: TAtomPosition); procedure ReadPriorUsedUnit(out UnitNameAtom, InAtom: TAtomPosition); // comments function FindCommentInFront(const StartPos: TCodeXYPosition; const CommentText: string; InvokeBuildTree, SearchInParentNode, WithCommentBounds, CaseSensitive, IgnoreSpaces, CompareOnlyStart: boolean; out CommentStart, CommentEnd: TCodeXYPosition): boolean; function FindCommentInFront(const StartPos: integer; const CommentText: string; SearchInParentNode, WithCommentBounds, CaseSensitive, IgnoreSpaces, CompareOnlyStart: boolean; out CommentStart, CommentEnd: integer): boolean; function CommentCode(const StartPos, EndPos: integer; SourceChangeCache: TSourceChangeCache; Apply: boolean): boolean; function GetPasDocComments(const StartPos: TCodeXYPosition; InvokeBuildTree: boolean; out ListOfPCodeXYPosition: TFPList): boolean; procedure CalcMemSize(Stats: TCTMemStats); override; end; implementation { TPascalReaderTool } function TPascalReaderTool.CleanPosIsInComment(CleanPos, CleanCodePosInFront: integer; var CommentStart, CommentEnd: integer ): boolean; var CommentLvl, CurCommentPos: integer; begin Result:=false; if CleanPos>SrcLen then exit; if CleanCodePosInFront>CleanPos then SaveRaiseException( 'TPascalReaderTool.CleanPosIsInComment CleanCodePosInFront>CleanPos'); MoveCursorToCleanPos(CleanCodePosInFront); repeat ReadNextAtom; if CurPos.StartPos>CleanPos then begin //DebugLn(['TPascalReaderTool.CleanPosIsInComment ',GetATom,' StartPos=',CurPos.StartPos,' CleanPos=',CleanPos]); // CleanPos between two atoms -> parse space between for comments CommentStart:=CleanCodePosInFront; CommentEnd:=CurPos.StartPos; if CommentEnd>SrcLen then CommentEnd:=SrcLen+1; while CommentStart0) do begin case Src[CurCommentPos] of '{': if Scanner.NestedComments then inc(CommentLvl); '}': dec(CommentLvl); end; inc(CurCommentPos); end; end; '/': // Delphi comment if (CurCommentPosSrc[CurCommentPos]) then inc(CurCommentPos); end else break; '(': // old turbo pascal comment if (CurCommentPos'*') or (Src[CurCommentPos]<>')')) do inc(CurCommentPos); inc(CurCommentPos); end else break; end; if (CurCommentPos>CommentStart) and (CleanPos=CommentEnd) or (not (IsSpaceChar[Src[CommentStart]])); end else begin break; end; end; // CleanPos not in a comment exit; end else if CurPos.EndPos>CleanPos then begin // CleanPos not in a comment exit; end; CleanCodePosInFront:=CurPos.EndPos; until CurPos.StartPos>=SrcLen; end; function TPascalReaderTool.ExtractPropType(PropNode: TCodeTreeNode; InUpperCase, EmptyIfIndexed: boolean): string; begin Result:=''; if (PropNode=nil) or ((PropNode.Desc<>ctnProperty) and (PropNode.Desc<>ctnGlobalProperty)) then exit; MoveCursorToNodeStart(PropNode); ReadNextAtom; if (PropNode.Desc=ctnProperty) then begin if UpAtomIs('CLASS') then ReadNextAtom; if (not UpAtomIs('PROPERTY')) then exit; ReadNextAtom; end; AtomIsIdentifier(true); ReadNextAtom; if CurPos.Flag=cafEdgedBracketOpen then begin if EmptyIfIndexed then exit; ReadTilBracketClose(true); ReadNextAtom; end; if CurPos.Flag in [cafSemicolon,cafEND] then exit; if not (CurPos.Flag=cafColon) then RaiseExceptionFmt(ctsStrExpectedButAtomFound,[':',GetAtom]); ReadNextAtom; AtomIsIdentifier(true); if InUpperCase then Result:=GetUpAtom else Result:=GetAtom; end; function TPascalReaderTool.ExtractProcName(ProcNode: TCodeTreeNode; Attr: TProcHeadAttributes): string; var ProcHeadNode: TCodeTreeNode; begin Result:=''; if [phpWithoutClassName,phpWithoutName]*Attr= [phpWithoutClassName,phpWithoutName] then exit; while (ProcNode<>nil) and (ProcNode.Desc<>ctnProcedure) do ProcNode:=ProcNode.Parent; if ProcNode=nil then exit; ProcHeadNode:=ProcNode.FirstChild; if (ProcHeadNode=nil) or (ProcHeadNode.StartPos<1) then exit; MoveCursorToNodeStart(ProcHeadNode); ReadNextAtom; if not AtomIsIdentifier(false) then exit; if phpInUpperCase in Attr then Result:=GetUpAtom else Result:=GetAtom; ReadNextAtom; if (CurPos.Flag=cafPoint) then begin if (phpWithoutClassName in Attr) then begin Result:=''; end else begin if not (phpWithoutName in Attr) then Result:=Result+'.'; end; ReadNextAtom; if not (phpWithoutName in Attr) then begin if phpInUpperCase in Attr then Result:=Result+GetUpAtom else Result:=Result+GetAtom; end; end else begin if phpWithoutName in Attr then Result:=''; end; end; function TPascalReaderTool.ExtractProcHead(ProcNode: TCodeTreeNode; Attr: TProcHeadAttributes): string; var TypeDefNode: TCodeTreeNode; TheClassName, s: string; HasClassName, IsProcType: boolean; IsProcedure: Boolean; IsFunction: Boolean; IsOperator: Boolean; const SemiColon : char = ';'; begin Result:=''; ExtractProcHeadPos:=phepNone; if (ProcNode=nil) or (ProcNode.StartPos<1) then exit; if ProcNode.Desc=ctnProcedureHead then begin ProcNode:=ProcNode.Parent; if ProcNode=nil then exit; end; if (ProcNode.Desc<>ctnProcedure) and (ProcNode.Desc<>ctnProcedureType) then exit; IsProcType:=(ProcNode.Desc=ctnProcedureType); if (phpAddClassname in Attr) then begin TheClassName:=''; TypeDefNode:=FindClassOrInterfaceNode(ProcNode); if TypeDefNode<>nil then begin TheClassName:=ExtractClassName(TypeDefNode,phpInUpperCase in Attr); end; end; InitExtraction; // reparse the clean source MoveCursorToNodeStart(ProcNode); // parse procedure head = start + name + parameterlist + result type ; ExtractNextAtom(false,Attr); // read procedure start keyword if (UpAtomIs('CLASS') or UpAtomIs('STATIC')) then ExtractNextAtom((phpWithStart in Attr) and not (phpWithoutClassKeyword in Attr),Attr); IsProcedure:=UpAtomIs('PROCEDURE'); IsFunction:=(not IsProcedure) and UpAtomIs('FUNCTION'); IsOperator:=(not IsProcedure) and (not IsFunction) and UpAtomIs('OPERATOR'); if IsProcedure or IsFunction or IsOperator or (UpAtomIs('CONSTRUCTOR')) or (UpAtomIs('DESTRUCTOR')) then ExtractNextAtom(phpWithStart in Attr,Attr) else exit; ExtractProcHeadPos:=phepStart; if not IsProcType then begin // read name if (not IsOperator) and (not AtomIsIdentifier(false)) then exit; ReadNextAtom; HasClassName:=(CurPos.Flag=cafPoint); UndoReadNextAtom; if HasClassName then begin // read class name ExtractNextAtom(not (phpWithoutClassName in Attr),Attr); // read '.' ExtractNextAtom(not (phpWithoutClassName in Attr),Attr); // read name if not AtomIsIdentifier(false) then exit; ExtractNextAtom(not (phpWithoutName in Attr),Attr); end else begin // read name if (not (phpAddClassname in Attr)) or (TheClassName='') then begin ExtractNextAtom(not (phpWithoutName in Attr),Attr); end else begin // add class name s:=TheClassName+'.'; if not (phpWithoutName in Attr) then s:=s+GetAtom; ExtractNextAtom(false,Attr); if phpInUpperCase in Attr then s:=UpperCaseStr(s); if ExtractStreamEndIsIdentChar then s:=' '+s; ExtractMemStream.Write(s[1],length(s)); end; end; ExtractProcHeadPos:=phepName; end; // read parameter list if (CurPos.Flag=cafRoundBracketOpen) then ReadParamList(false,true,Attr); ExtractProcHeadPos:=phepParamList; if IsOperator and (CurPos.Flag=cafWord) then begin // read operator result name ExtractNextAtom([phpWithParameterNames,phpWithResultType]*Attr =[phpWithParameterNames,phpWithResultType],Attr); end; // read result type if (CurPos.Flag=cafColon) then begin ExtractNextAtom(phpWithResultType in Attr,Attr); if not AtomIsIdentifier(false) then exit; ExtractNextAtom(phpWithResultType in Attr,Attr); if CurPos.Flag=cafPoint then begin ExtractNextAtom(phpWithResultType in Attr,Attr); if not AtomIsIdentifier(false) then exit; ExtractNextAtom(phpWithResultType in Attr,Attr); end; ExtractProcHeadPos:=phepResultType; end; // read 'of object' if UpAtomIs('OF') then begin if IsProcType then begin ExtractNextAtom(phpWithOfObject in Attr,Attr); if not UpAtomIs('OBJECT') then exit; ExtractNextAtom(phpWithOfObject in Attr,Attr); end; end; // read semicolon if CurPos.Flag=cafSemicolon then ExtractNextAtom(not (phpWithoutSemicolon in Attr),Attr); // read specifiers if [phpWithCallingSpecs,phpWithProcModifiers]*Attr<>[] then begin while (CurPos.StartPos[], Attr); if not (phpWithProcModifiers in Attr) then ExtractMemStream.Write(SemiColon,1); end else if (CurPos.Flag=cafEdgedBracketOpen) then begin ReadTilBracketClose(false); ExtractNextAtom(phpWithProcModifiers in Attr,Attr); end else begin ExtractNextAtom(phpWithProcModifiers in Attr,Attr); end; end; end; end; // copy memorystream to Result string Result:=GetExtraction(phpInUpperCase in Attr); // add semicolon if ([phpWithoutSemicolon,phpDoNotAddSemicolon]*Attr=[]) and (Result<>'') and (Result[length(Result)]<>';') then Result:=Result+';'; end; function TPascalReaderTool.ExtractClassName(ClassNode: TCodeTreeNode; InUpperCase: boolean): string; var DefNode: TCodeTreeNode; begin if ClassNode<>nil then begin ClassNode:=FindClassOrInterfaceNode(ClassNode); if (ClassNode = nil) then begin Result := ''; Exit; end; DefNode:=ClassNode.Parent; if (DefNode<>nil) and (DefNode.Desc=ctnGenericType) then DefNode:=DefNode.FirstChild; if DefNode=nil then begin Result:=''; exit; end; if InUpperCase then Result:=UpperCaseStr(GetIdentifier(@Src[DefNode.StartPos])) else Result:=GetIdentifier(@Src[DefNode.StartPos]); end else Result:=''; end; function TPascalReaderTool.ExtractClassInheritance( ClassNode: TCodeTreeNode; Attr: TProcHeadAttributes): string; begin Result:=''; if (ClassNode=nil) or (not (ClassNode.Desc in AllClasses)) then exit; MoveCursorToNodeStart(ClassNode); ReadNextAtom; // class if UpAtomIs('PACKED') then ReadNextAtom; if not (UpAtomIs('CLASS') or UpAtomIs('OBJECT') or UpAtomIs('OBJCLASS') or (UpAtomIs('INTERFACE'))) then exit; ReadNextAtom; // '(' if CurPos.Flag<>cafRoundBracketOpen then exit; ReadNextAtom; if not AtomIsIdentifier(false) then exit; MoveCursorToCleanPos(CurPos.StartPos); ExtractProcHeadPos:=phepNone; InitExtraction; while (CurPos.StartPos<=SrcLen) do begin ExtractNextAtom(true,Attr); // read ancestor/interface if not AtomIsIdentifier(false) then break; ExtractNextAtom(true,Attr); // read ',' if not AtomIsChar(',') then break; end; // copy memorystream to Result string Result:=GetExtraction(phpInUpperCase in Attr); end; function TPascalReaderTool.ExtractClassNameOfProcNode(ProcNode: TCodeTreeNode ): string; var TheClassName: string; begin Result:=''; if (ProcNode<>nil) and (ProcNode.Desc=ctnProcedure) then ProcNode:=ProcNode.FirstChild; if (ProcNode=nil) or (ProcNode.Desc<>ctnProcedureHead) then exit; MoveCursorToNodeStart(ProcNode); ReadNextAtom; if not AtomIsIdentifier(false) then exit; TheClassName:=GetAtom; ReadNextAtom; if (CurPos.Flag<>cafPoint) then exit; ReadNextAtom; if not AtomIsIdentifier(false) then exit; Result:=TheClassName; end; function TPascalReaderTool.FindProcNode(StartNode: TCodeTreeNode; const AProcHead: string; Attr: TProcHeadAttributes): TCodeTreeNode; // search in all next brothers for a Procedure Node with the Name ProcName // if there are no further brothers and the parent is a section node // ( e.g. 'interface', 'implementation', ...) or a class visibility node // (e.g. 'public', 'private', ...) then the search will continue in the next // section var CurProcHead: string; begin Result:=StartNode; while (Result<>nil) do begin //DebugLn('TPascalReaderTool.FindProcNode A "',NodeDescriptionAsString(Result.Desc),'"'); if Result.Desc=ctnProcedure then begin if (not ((phpIgnoreForwards in Attr) and ((Result.SubDesc and ctnsForwardDeclaration)>0))) and (not ((phpIgnoreProcsWithBody in Attr) and (FindProcBody(Result)<>nil))) then begin CurProcHead:=ExtractProcHead(Result,Attr); //DebugLn('TPascalReaderTool.FindProcNode B "',CurProcHead,'" =? "',AProcHead,'"'); if (CurProcHead<>'') and (CompareTextIgnoringSpace(CurProcHead,AProcHead,false)=0) then exit; end; end; // next node Result:=FindNextNodeOnSameLvl(Result); end; end; function TPascalReaderTool.FindCorrespondingProcNode(ProcNode: TCodeTreeNode; Attr: TProcHeadAttributes): TCodeTreeNode; var ClassNode: TCodeTreeNode; StartNode: TCodeTreeNode; ProcHead: String; begin Result:=nil; // get ctnProcedure //debugln('TPascalReaderTool.FindCorrespondingProcNode Start'); if (ProcNode=nil) then exit; if ProcNode.Desc=ctnProcedureHead then begin ProcNode:=ProcNode.Parent; if (ProcNode=nil) then exit; end; if ProcNode.Desc<>ctnProcedure then exit; // check proc kind //debugln('TPascalReaderTool.FindCorrespondingProcNode Check kind'); ClassNode:=FindClassOrInterfaceNode(ProcNode); if ClassNode<>nil then begin //debugln('TPascalReaderTool.FindCorrespondingProcNode Class'); // in a class definition -> search method body StartNode:=ClassNode.GetNodeOfType(ctnTypeSection) end else if NodeIsMethodBody(ProcNode) then begin //debugln('TPascalReaderTool.FindCorrespondingProcNode Method ',ExtractClassNameOfProcNode(ProcNode)); // in a method body -> search in class StartNode:=FindClassNodeInUnit(ExtractClassNameOfProcNode(ProcNode),true, false,false,true); if StartNode=nil then exit; BuildSubTreeForClass(StartNode); if (StartNode<>nil) and (StartNode.Desc in AllClasses) then begin StartNode:=StartNode.FirstChild; while (StartNode<>nil) do begin if (StartNode.Desc in AllClassBaseSections) and (StartNode.FirstChild<>nil) then begin StartNode:=StartNode.FirstChild; break; end; StartNode:=StartNode.NextBrother; end; end; end else begin //DebugLn('TPascalReaderTool.FindCorrespondingProcNode Normal'); // else: search on same lvl StartNode:=FindFirstNodeOnSameLvl(ProcNode); end; if StartNode=nil then exit; ProcHead:=ExtractProcHead(ProcNode,Attr); //debugln('TPascalReaderTool.FindCorrespondingProcNode StartNode=',StartNode.DescAsString,' ProcHead=',dbgstr(ProcHead)); Result:=FindProcNode(StartNode,ProcHead,Attr); if Result=ProcNode then begin // found itself -> search further StartNode:=FindNextNodeOnSameLvl(Result); Result:=FindProcNode(StartNode,ProcHead,Attr); end; //if Result<>nil then debugln(['TPascalReaderTool.FindCorrespondingProcNode Result=',CleanPosToStr(Result.StartPos),' ',dbgstr(copy(Src,Result.StartPos,50))]); end; function TPascalReaderTool.FindCorrespondingProcParamNode( ProcParamNode: TCodeTreeNode; Attr: TProcHeadAttributes): TCodeTreeNode; var ProcNode: TCodeTreeNode; begin Result:=nil; if ProcParamNode=nil then exit; if (ProcParamNode.Desc=ctnVarDefinition) and (ProcParamNode.Parent.Desc=ctnParameterList) and (ProcParamNode.Parent.Parent.Desc=ctnProcedureHead) then begin // this is a parameter name ProcNode:=ProcParamNode.GetNodeOfType(ctnProcedure); if ProcNode=nil then exit; // search alias for parameter ProcNode:=FindCorrespondingProcNode(ProcNode,Attr); if ProcNode=nil then exit; BuildSubTreeForProcHead(ProcNode); Result:=ProcNode; while (Result<>nil) do begin //debugln(['TPascalReaderTool.FindCorrespondingProcParamNode ',dbgstr(copy(Src,Result.StartPos,20))]); if Result.Desc in [ctnProcedure,ctnProcedureHead,ctnParameterList] then Result:=Result.FirstChild else begin if Result.StartPos<1 then break; if CompareIdentifiers(@Src[ProcParamNode.StartPos],@Src[Result.StartPos])=0 then exit; Result:=Result.NextBrother; end; end; Result:=nil; end; end; function TPascalReaderTool.FindProcBody(ProcNode: TCodeTreeNode ): TCodeTreeNode; begin Result:=ProcNode; if Result=nil then exit; if Result.Desc<>ctnProcedure then exit; Result:=Result.FirstChild; while Result<>nil do begin if Result.Desc in [ctnBeginBlock,ctnAsmBlock] then exit; Result:=Result.NextBrother; end; end; function TPascalReaderTool.ProcBodyIsEmpty(ProcNode: TCodeTreeNode): boolean; var BodyNode: TCodeTreeNode; LastPos: LongInt; begin Result:=false; BodyNode:=FindProcBody(ProcNode); if (BodyNode=nil) then exit; // check if there are nodes in front (e.g. local variables) if (BodyNode.PriorBrother<>nil) and (BodyNode.PriorBrother.Desc<>ctnProcedureHead) then exit; // check if there are child nodes if BodyNode.FirstChild<>nil then exit; // check if bodynode is only 'asm end' or 'begin end' // not even a comment should be there, only spaces are allowed if ProcNode.FirstChild.Desc<>ctnProcedureHead then exit; MoveCursorToCleanPos(ProcNode.FirstChild.EndPos); LastPos:=CurPos.EndPos; ReadNextAtom; if FindNextNonSpace(Src,LastPos)<>CurPos.StartPos then exit; if CurPos.Flag=cafSemicolon then begin // semicolon is allowed LastPos:=CurPos.EndPos; ReadNextAtom; if FindNextNonSpace(Src,LastPos)<>CurPos.StartPos then exit; end; if not (UpAtomIs('ASM') or UpAtomIs('BEGIN')) then exit; LastPos:=CurPos.EndPos; ReadNextAtom; if FindNextNonSpace(Src,LastPos)<>CurPos.StartPos then exit; if not UpAtomIs('END') then exit; Result:=true; end; procedure TPascalReaderTool.MoveCursorToFirstProcSpecifier( ProcNode: TCodeTreeNode); // After the call, // CurPos will stand on the first proc specifier or on a semicolon begin //DebugLn(['TPascalReaderTool.MoveCursorToFirstProcSpecifier ',ProcNode.DescAsString,' ',ProcNode.StartPos]); if (ProcNode=nil) or (ProcNode.Desc<>ctnProcedure) then begin SaveRaiseException('Internal Error in' +' TPascalParserTool.MoveCursorFirstProcSpecifier: ' +' (ProcNode=nil) or (ProcNode.Desc<>ctnProcedure)'); end; MoveCursorToNodeStart(ProcNode.FirstChild); ReadNextAtom; if AtomIsIdentifier(false) then begin // read name ReadNextAtom; if (CurPos.Flag=cafPoint) then begin // read method name ReadNextAtom; ReadNextAtom; end; end; if (CurPos.Flag=cafRoundBracketOpen) then begin // read paramlist ReadTilBracketClose(false); ReadNextAtom; end; if (CurPos.Flag=cafColon) then begin // read function result type ReadNextAtom; if AtomIsIdentifier(false) then begin ReadNextAtom; if CurPos.Flag=cafPoint then begin ReadNextAtom; if AtomIsIdentifier(false) then ReadNextAtom; end; end; end; // CurPos now stands on the first proc specifier or on a semicolon end; function TPascalReaderTool.MoveCursorToProcSpecifier(ProcNode: TCodeTreeNode; ProcSpec: TProcedureSpecifier): boolean; begin MoveCursorToFirstProcSpecifier(ProcNode); while (CurPos.StartPos<=ProcNode.FirstChild.EndPos) do begin if CurPos.Flag=cafSemicolon then begin ReadNextAtom; end else begin if UpAtomIs(ProcedureSpecifierNames[ProcSpec]) then begin Result:=true; exit; end; if (CurPos.Flag=cafEdgedBracketOpen) then begin ReadTilBracketClose(false); ReadNextAtom; end else if UpAtomIs('MESSAGE') then begin ReadNextAtom; ReadConstant(true,false,[]); end else if UpAtomIs('EXTERNAL') then begin ReadNextAtom; if CurPos.Flag<>cafSemicolon then begin if not UpAtomIs('NAME') then ReadConstant(true,false,[]); if UpAtomIs('NAME') or UpAtomIs('INDEX') then begin ReadNextAtom; ReadConstant(true,false,[]); end; end; end else begin ReadNextAtom; end; end; end; Result:=false; end; procedure TPascalReaderTool.MoveCursorToProcName(ProcNode: TCodeTreeNode; SkipClassName: boolean); begin if (ProcNode.Desc=ctnProcedure) and (ProcNode.FirstChild<>nil) and (ProcNode.FirstChild.Desc=ctnProcedureHead) then ProcNode:=ProcNode.FirstChild; MoveCursorToNodeStart(ProcNode); ReadNextAtom; if (ProcNode.Desc=ctnProcedure) then begin if UpAtomIs('CLASS') then ReadNextAtom; ReadNextAtom; // skip proc keyword end; if SkipClassName then begin ReadNextAtom; if CurPos.Flag=cafPoint then ReadNextAtom else UndoReadNextAtom; end; end; function TPascalReaderTool.PositionInProcName(ProcNode: TCodeTreeNode; SkipClassName: boolean; CleanPos: integer): boolean; var InFirstAtom: Boolean; begin if (ProcNode.Desc=ctnProcedure) and (ProcNode.FirstChild<>nil) and (ProcNode.FirstChild.Desc=ctnProcedureHead) then ProcNode:=ProcNode.FirstChild; MoveCursorToNodeStart(ProcNode); ReadNextAtom; if (ProcNode.Desc=ctnProcedure) then begin if UpAtomIs('CLASS') then ReadNextAtom; ReadNextAtom; // skip proc keyword end; if CurPos.Flag<>cafWord then exit(false); // now CurPos is either the classname or the procname InFirstAtom:=(CleanPos>=CurPos.StartPos) and (CleanPos<=CurPos.EndPos); ReadNextAtom; // read point if CurPos.Flag<>cafPoint then begin // procname without classname exit(InFirstAtom); end; // there is a classname if (CleanPos>=CurPos.StartPos) and (CleanPos<=CurPos.EndPos) and (not SkipClassName) then exit(true); // position at point // now read the procname ReadNextAtom; if CurPos.Flag<>cafWord then exit(false); // no valid procname if (CleanPos>=CurPos.StartPos) and (CleanPos<=CurPos.EndPos) then exit(true); // position at procname if (not SkipClassName) and InFirstAtom then exit(true); // position at classname Result:=false; end; function TPascalReaderTool.PositionInFuncResultName(ProcNode: TCodeTreeNode; CleanPos: integer): boolean; // true if position between ) and : begin Result:=false; if ProcNode=nil then exit; if ProcNode.Desc=ctnProcedure then begin ProcNode:=ProcNode.FirstChild; if ProcNode=nil then exit; end; if (ProcNode.Desc in [ctnIdentifier,ctnVarDefinition]) and (ProcNode.Parent<>nil) and (ProcNode.Parent.Desc=ctnProcedureHead) and (CleanPos>=ProcNode.StartPos) and (CleanPos<=ProcNode.EndPos) then begin exit(true); end; // read behind parameter list if ProcNode.Desc<>ctnProcedureHead then exit; if (ProcNode.FirstChild<>nil) and (ProcNode.FirstChild.Desc=ctnParameterList) then begin if (CleanPosCleanPos then exit; // read optional result variable (e.g. operator can have them) ReadNextAtom; if AtomIsIdentifier(false) then ReadNextAtom; if CurPos.Flag<>cafColon then exit; Result:=CleanPos<=CurPos.StartPos; end; function TPascalReaderTool.MoveCursorToPropType(PropNode: TCodeTreeNode ): boolean; begin Result:=false; if (PropNode=nil) or ((PropNode.Desc<>ctnProperty) and (PropNode.Desc<>ctnGlobalProperty)) then exit; MoveCursorToNodeStart(PropNode); ReadNextAtom; if (PropNode.Desc=ctnProperty) then begin if UpAtomIs('CLASS') then ReadNextAtom; if (not UpAtomIs('PROPERTY')) then exit; ReadNextAtom; end; if not AtomIsIdentifier(false) then exit; ReadNextAtom; if CurPos.Flag=cafEdgedBracketOpen then begin ReadTilBracketClose(true); ReadNextAtom; end; if CurPos.Flag in [cafSemicolon,cafEND] then exit; if CurPos.Flag<>cafColon then exit; ReadNextAtom; Result:=CurPos.Flag=cafWord; end; function TPascalReaderTool.MoveCursorToPropName(PropNode: TCodeTreeNode ): boolean; begin Result:=false; if (PropNode=nil) or ((PropNode.Desc<>ctnProperty) and (PropNode.Desc<>ctnGlobalProperty)) then exit; MoveCursorToNodeStart(PropNode); ReadNextAtom; if (PropNode.Desc=ctnProperty) then begin if UpAtomIs('CLASS') then ReadNextAtom; if (not UpAtomIs('PROPERTY')) then exit; ReadNextAtom; end; Result:=CurPos.Flag=cafWord; end; function TPascalReaderTool.ProcNodeHasSpecifier(ProcNode: TCodeTreeNode; ProcSpec: TProcedureSpecifier): boolean; begin // ToDo: ppu, dcu Result:=MoveCursorToProcSpecifier(ProcNode,ProcSpec); end; function TPascalReaderTool.GetProcNameIdentifier(ProcNode: TCodeTreeNode ): PChar; begin // ToDo: ppu, dcu Result:=nil; if ProcNode=nil then exit; if ProcNode.Desc=ctnProcedure then begin ProcNode:=ProcNode.FirstChild; if ProcNode=nil then exit; end; MoveCursorToNodeStart(ProcNode); ReadNextAtom; if not AtomIsIdentifier(false) then exit; Result:=@Src[CurPos.StartPos]; ReadNextAtom; if not AtomIsChar('.') then exit; ReadNextAtom; Result:=@Src[CurPos.StartPos]; end; function TPascalReaderTool.ExtractNode(ANode: TCodeTreeNode; Attr: TProcHeadAttributes): string; begin Result:=''; ExtractProcHeadPos:=phepNone; if (ANode=nil) or (ANode.StartPos<1) then exit; InitExtraction; // reparse the clean source MoveCursorToNodeStart(ANode); while (ANode.EndPos>CurPos.StartPos) and (CurPos.StartPos<=SrcLen) do ExtractNextAtom(true,Attr); // copy memorystream to Result string Result:=GetExtraction(phpInUpperCase in Attr); end; function TPascalReaderTool.ExtractCode(StartPos, EndPos: integer; Attr: TProcHeadAttributes): string; begin Result:=''; ExtractProcHeadPos:=phepNone; if (StartPos<1) or (StartPos>=EndPos) or (StartPos>SrcLen) then exit; InitExtraction; // reparse the clean source MoveCursorToCleanPos(StartPos); while (EndPos>CurPos.StartPos) and (CurPos.StartPos<=SrcLen) do ExtractNextAtom(true,Attr); // copy memorystream to Result string Result:=GetExtraction(phpInUpperCase in Attr); end; function TPascalReaderTool.ExtractBrackets(BracketStartPos: integer; Attr: TProcHeadAttributes): string; function ExtractTilBracketClose(ExtractBrackets: boolean): boolean; var CloseBracket: TCommonAtomFlag; First: Boolean; begin Result:=true; case CurPos.Flag of cafRoundBracketOpen: CloseBracket:=cafRoundBracketClose; cafEdgedBracketOpen: CloseBracket:=cafEdgedBracketClose; else exit; end; First:=true; repeat if First then ExtractNextAtom(ExtractBrackets,Attr) else ExtractNextAtom(true,Attr); if CurPos.StartPos>SrcLen then exit; if CurPos.Flag=CloseBracket then exit(true); if CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen] then begin if not ExtractTilBracketClose(true) then exit; end; until false; end; begin Result:=''; ExtractProcHeadPos:=phepNone; if (BracketStartPos<1) or (BracketStartPos>SrcLen) then exit; InitExtraction; // reparse the clean source MoveCursorToCleanPos(BracketStartPos); ReadNextAtom; if not ExtractTilBracketClose(not (phpWithoutBrackets in Attr)) then exit; if not (phpWithoutBrackets in Attr) then ExtractNextAtom(true,Attr); // copy memorystream to Result string Result:=GetExtraction(phpInUpperCase in Attr); end; function TPascalReaderTool.ExtractPropName(PropNode: TCodeTreeNode; InUpperCase: boolean): string; begin Result:=''; if not MoveCursorToPropName(PropNode) then exit; if InUpperCase then Result:=GetUpAtom else Result:=GetAtom; end; function TPascalReaderTool.ExtractProperty(PropNode: TCodeTreeNode; Attr: TProcHeadAttributes): string; begin Result:=''; ExtractProcHeadPos:=phepNone; if (PropNode=nil) or (PropNode.StartPos<1) or ((PropNode.Desc<>ctnProperty) and (PropNode.Desc<>ctnGlobalProperty)) then exit; // start extraction InitExtraction; MoveCursorToNodeStart(PropNode); ExtractNextAtom(false,Attr); if (PropNode.Desc=ctnProperty) then begin if UpAtomIs('CLASS') then ExtractNextAtom(phpWithStart in Attr,Attr); // parse 'property' ExtractNextAtom(phpWithStart in Attr,Attr); end; ExtractProcHeadPos:=phepStart; // parse name ExtractNextAtom(not (phpWithoutName in Attr),Attr); ExtractProcHeadPos:=phepName; // read parameter list if (CurPos.Flag=cafEdgedBracketOpen) then ReadParamList(false,true,Attr); ExtractProcHeadPos:=phepParamList; // read result type if (CurPos.Flag=cafColon) then begin ExtractNextAtom(phpWithResultType in Attr,Attr); if not AtomIsIdentifier(false) then exit; ExtractNextAtom(phpWithResultType in Attr,Attr); if CurPos.Flag=cafPoint then begin // unit.type ExtractNextAtom(phpWithResultType in Attr,Attr); if not AtomIsIdentifier(false) then exit; ExtractNextAtom(phpWithResultType in Attr,Attr); end; ExtractProcHeadPos:=phepResultType; end; // copy memorystream to Result string Result:=GetExtraction(phpInUpperCase in Attr); end; function TPascalReaderTool.GetPropertyNameIdentifier(PropNode: TCodeTreeNode ): PChar; begin // ToDo: ppu, dcu Result:=nil; if PropNode=nil then exit; if not MoveCursorToPropName(PropNode) then exit; Result:=@Src[CurPos.StartPos]; end; function TPascalReaderTool.GetPropertyTypeIdentifier(PropNode: TCodeTreeNode ): PChar; begin // ToDo: ppu, dcu Result:=nil; if PropNode=nil then exit; if not MoveCursorToPropType(PropNode) then exit; Result:=@Src[CurPos.StartPos]; end; function TPascalReaderTool.PositionInPropertyName(PropNode: TCodeTreeNode; CleanPos: integer): boolean; begin if PropNode=nil then exit(false); MoveCursorToNodeStart(PropNode); if (PropNode.Desc=ctnProperty) then begin ReadNextAtom; // read 'property' if UpAtomIs('CLASS') then ReadNextAtom; end; ReadNextAtom; // read name Result:=(CurPos.Flag=cafWord) and (CleanPos>=CurPos.StartPos) and (CleanPos<=CurPos.EndPos); end; function TPascalReaderTool.ExtractIdentCharsFromStringConstant(StartPos, MinPos, MaxPos, MaxLen: integer): string; var APos: Integer; IdentStartPos: Integer; IdentStr: String; IdentEndPos: LongInt; begin Result:=''; APos:=StartPos; while APosMaxPos then IdentEndPos:=MaxPos; if (IdentEndPos>IdentStartPos) then begin if IdentEndPos-IdentStartPos+length(Result)>MaxLen then IdentEndPos:=IdentStartPos+MaxLen-length(Result); IdentStr:=copy(Src,IdentStartPos,IdentEndPos-IdentStartPos); if (IdentStr<>'') then begin IdentStr[1]:=UpChars[IdentStr[1]]; Result:=Result+IdentStr; end; end; // skip non identifier chars while (APos'''') and (not IsIdentChar[Src[APos]]) do inc(APos); until (APos>=SrcLen) or (Src[APos]='''') or (length(Result)>=MaxLen); inc(APos); end else break; end; end; function TPascalReaderTool.ReadStringConstantValue(StartPos: integer): string; // reads a string constant and returns the resulting string var APos: Integer; Run: Integer; NumberStart: Integer; ResultLen: Integer; Number: Integer; begin Result:=''; // first read and calculate the resulting length, then copy the chars for Run:=1 to 2 do begin APos:=StartPos; ResultLen:=0; while APos<=SrcLen do begin if Src[APos]='''' then begin // read string inc(APos); while APos<=SrcLen do begin if (Src[APos]='''') then begin if (APos255) then break; inc(ResultLen); if Run=2 then Result[ResultLen]:=chr(Number); end; end else break; end; if Run=1 then SetLength(Result,ResultLen); end; end; function TPascalReaderTool.GetNodeIdentifier(Node: TCodeTreeNode): PChar; begin Result:=nil; if Node=nil then exit; case Node.Desc of ctnProcedure,ctnProcedureHead: Result:=GetProcNameIdentifier(Node); ctnProperty: Result:=GetPropertyNameIdentifier(Node); ctnTypeDefinition,ctnVarDefinition,ctnConstDefinition, ctnEnumIdentifier,ctnIdentifier: Result:=@Src[Node.StartPos]; end; end; function TPascalReaderTool.FindVarNode(StartNode: TCodeTreeNode; const UpperVarName: string): TCodeTreeNode; begin Result:=StartNode; while Result<>nil do begin if (Result.Desc=ctnVarDefinition) and (CompareNodeIdentChars(Result,UpperVarName)=0) then exit; Result:=FindNextNodeOnSameLvl(Result); end; end; function TPascalReaderTool.FindTypeNodeOfDefinition( DefinitionNode: TCodeTreeNode): TCodeTreeNode; // for example: 'var a,b,c: integer;' only c has a type child begin Result:=DefinitionNode; while (Result<>nil) and (Result.Desc in AllIdentifierDefinitions) do begin if (Result.FirstChild<>nil) then begin Result:=Result.FirstChild; if Result.Desc=ctnGenericName then begin // skip generic name and params Result:=Result.NextBrother; if Result=nil then exit; Result:=Result.NextBrother; if Result=nil then exit; end; if (not (Result.Desc in AllPascalTypes)) then Result:=nil; exit; end; if Result.Desc=ctnConstDefinition then exit(nil); Result:=Result.NextBrother; end; end; function TPascalReaderTool.FindClassNode(StartNode: TCodeTreeNode; const AClassName: string; IgnoreForwards, IgnoreNonForwards: boolean ): TCodeTreeNode; // search for types on same level, // with type class and classname = SearchedClassName var ANode, CurClassNode: TCodeTreeNode; NameNode: TCodeTreeNode; begin ANode:=StartNode; Result:=nil; while (ANode<>nil) do begin if ANode.Desc in [ctnTypeDefinition,ctnGenericType] then begin CurClassNode:=FindTypeNodeOfDefinition(ANode); if (CurClassNode<>nil) and (CurClassNode.Desc in AllClassObjects) then begin if (not (IgnoreForwards and ((CurClassNode.SubDesc and ctnsForwardDeclaration)>0))) and (not (IgnoreNonForwards and ((CurClassNode.SubDesc and ctnsForwardDeclaration)=0))) then begin NameNode:=ANode; if ANode.Desc=ctnGenericType then NameNode:=ANode.FirstChild; if CompareIdentifiers(PChar(Pointer(AClassName)), @Src[NameNode.StartPos])=0 then begin Result:=CurClassNode; exit; end; end; end; end; // next node if (ANode.Desc in [ctnTypeSection]+AllCodeSections) and (ANode.FirstChild<>nil) then ANode:=ANode.FirstChild else if ANode.NextBrother<>nil then ANode:=ANode.NextBrother else begin // skip procs, const and var sections repeat ANode:=ANode.Parent; if (ANode=nil) then exit; if (not (ANode.Desc in [ctnTypeSection]+AllCodeSections)) then exit; if ANode.NextBrother<>nil then begin ANode:=ANode.NextBrother; break; end; until false; end; end; end; function TPascalReaderTool.FindClassNodeBackwards(StartNode: TCodeTreeNode; const AClassName: string; IgnoreForwards, IgnoreNonForwards: boolean ): TCodeTreeNode; var ANode: TCodeTreeNode; CurClassNode: TCodeTreeNode; begin ANode:=StartNode; while ANode<>nil do begin if ANode.Desc=ctnTypeDefinition then begin CurClassNode:=ANode.FirstChild; if (CurClassNode<>nil) and (CurClassNode.Desc in AllClassObjects) then begin if (not (IgnoreForwards and ((CurClassNode.SubDesc and ctnsForwardDeclaration)>0))) and (not (IgnoreNonForwards and ((CurClassNode.SubDesc and ctnsForwardDeclaration)=0))) then begin if CompareIdentifiers(PChar(Pointer(AClassName)), @Src[ANode.StartPos])=0 then begin Result:=CurClassNode; exit; end; end; end; end; if ANode.PriorBrother<>nil then begin ANode:=ANode.PriorBrother; if (ANode.FirstChild<>nil) and (ANode.Desc in AllCodeSections) then ANode:=ANode.LastChild; if (ANode.FirstChild<>nil) and (ANode.Desc in AllDefinitionSections) then ANode:=ANode.LastChild; end else begin ANode:=ANode.Parent; end; end; Result:=nil; end; function TPascalReaderTool.FindClassNode(CursorNode: TCodeTreeNode ): TCodeTreeNode; begin while CursorNode<>nil do begin if CursorNode.Desc in AllClassObjects then begin Result:=CursorNode; exit; end else if NodeIsMethodBody(CursorNode) then begin Result:=FindClassNodeForMethodBody(CursorNode,true,false); exit; end; CursorNode:=CursorNode.Parent; end; Result:=nil; end; function TPascalReaderTool.FindClassNodeForMethodBody(ProcNode: TCodeTreeNode; IgnoreForwards, IgnoreNonForwards: boolean): TCodeTreeNode; var ProcClassName: String; begin Result:=nil; ProcClassName:=ExtractClassNameOfProcNode(ProcNode); if ProcClassName='' then exit; Result:=FindClassNodeBackwards(ProcNode,ProcClassName,IgnoreForwards, IgnoreNonForwards); end; function TPascalReaderTool.FindClassOrInterfaceNode(CursorNode: TCodeTreeNode; FindClassOfMethod: boolean): TCodeTreeNode; begin while CursorNode<>nil do begin if CursorNode.Desc in AllClasses then begin Result:=CursorNode; exit; end else if FindClassOfMethod and NodeIsMethodBody(CursorNode) then begin Result:=FindClassNodeForMethodBody(CursorNode,true,false); exit; end; CursorNode:=CursorNode.Parent; end; Result:=nil; end; function TPascalReaderTool.FindClassSection(ClassNode: TCodeTreeNode; NodeDesc: TCodeTreeNodeDesc): TCodeTreeNode; begin Result:=ClassNode.FirstChild; while (Result<>nil) and (Result.Desc<>NodeDesc) do Result:=Result.NextBrother; end; function TPascalReaderTool.FindLastClassSection(ClassNode: TCodeTreeNode; NodeDesc: TCodeTreeNodeDesc): TCodeTreeNode; begin Result:=ClassNode.LastChild; while (Result<>nil) and (Result.Desc<>NodeDesc) do Result:=Result.PriorBrother; end; function TPascalReaderTool.FindClassNodeInInterface( const AClassName: string; IgnoreForwards, IgnoreNonForwards, ErrorOnNotFound: boolean): TCodeTreeNode; procedure RaiseClassNotFound; begin RaiseExceptionFmt(ctsClassSNotFound, [AClassName]); end; begin Result:=Tree.Root; if Result<>nil then begin if Result.Desc=ctnUnit then begin Result:=Result.NextBrother; end; if Result<>nil then begin Result:=FindClassNode(Result.FirstChild,AClassName, IgnoreForwards, IgnoreNonForwards); if (Result<>nil) and Result.HasParentOfType(ctnImplementation) then Result:=nil; end; end; if (Result=nil) and ErrorOnNotFound then RaiseClassNotFound; end; function TPascalReaderTool.FindClassNodeInUnit(const AClassName: string; IgnoreForwards, IgnoreNonForwards, IgnoreImplementation, ErrorOnNotFound: boolean): TCodeTreeNode; procedure RaiseClassNotFound; begin RaiseExceptionFmt(ctsClassSNotFound, [AClassName]); end; begin Result:=Tree.Root; if Result<>nil then begin if Result.Desc in [ctnUnit,ctnLibrary,ctnPackage] then begin Result:=Result.NextBrother; end; if Result<>nil then begin Result:=FindClassNode(Result.FirstChild,AClassName, IgnoreForwards, IgnoreNonForwards); if (Result<>nil) and IgnoreImplementation and Result.HasParentOfType(ctnImplementation) then Result:=nil; end; end; if (Result=nil) and ErrorOnNotFound then RaiseClassNotFound; end; function TPascalReaderTool.FindFirstIdentNodeInClass(ClassNode: TCodeTreeNode ): TCodeTreeNode; begin Result:=nil; if (ClassNode=nil) then exit; BuildSubTreeForClass(ClassNode); Result:=ClassNode.FirstChild; while (Result<>nil) and (Result.FirstChild=nil) do Result:=Result.NextBrother; if Result=nil then exit; Result:=Result.FirstChild; end; function TPascalReaderTool.ClassSectionNodeStartsWithWord(ANode: TCodeTreeNode ): boolean; var p: integer; begin Result:=false; if ANode=nil then exit; p:=ANode.StartPos; while (pnil) and (Node.Desc=ctnClass); end; function TPascalReaderTool.FindInheritanceNode(ClassNode: TCodeTreeNode ): TCodeTreeNode; begin Result:=ClassNode.FirstChild; while (Result<>nil) and (Result.Desc in [ctnClassSealed,ctnClassAbstract]) do Result:=Result.NextBrother; if (Result<>nil) and (Result.Desc<>ctnClassInheritance) then Result:=nil; end; function TPascalReaderTool.ExtractRecordCaseType(RecordCaseNode: TCodeTreeNode ): string; begin MoveCursorToNodeStart(RecordCaseNode); ReadNextAtom;// case ReadNextAtom;// identifier ReadNextAtom;// : if AtomIsChar(':') then begin ReadNextAtom; AtomIsIdentifier(true); Result:=GetAtom; end else begin Result:=''; end; end; function TPascalReaderTool.GetSourceType: TCodeTreeNodeDesc; begin if Tree.Root<>nil then Result:=Tree.Root.Desc else Result:=ctnNone; end; function TPascalReaderTool.GetSourceNamePos(var NamePos: TAtomPosition ): boolean; begin Result:=false; NamePos.StartPos:=-1; if Tree.Root=nil then exit; MoveCursorToNodeStart(Tree.Root); ReadNextAtom; // read source type 'program', 'unit' ... ReadNextAtom; // read name NamePos:=CurPos; Result:=(NamePos.StartPos<=SrcLen); end; function TPascalReaderTool.PositionInSourceName(CleanPos: integer): boolean; begin Result:=false; if Tree.Root=nil then exit; MoveCursorToNodeStart(Tree.Root); ReadNextAtom; // read source type 'program', 'unit' ... ReadNextAtom; // read name Result:=(CleanPos>=CurPos.StartPos) and (CleanPosnil) do begin if (Node.Desc=ctnProcedure) then begin if NodeIsMethodBody(Node) then begin Result:=true; exit; end; end; Node:=Node.Parent; end; end; function TPascalReaderTool.NodeIsMethodBody(ProcNode: TCodeTreeNode): boolean; begin Result:=false; if (ProcNode<>nil) and (ProcNode.Desc=ctnProcedure) and (ProcNode.FirstChild<>nil) then begin // ToDo: ppu, dcu MoveCursorToNodeStart(ProcNode.FirstChild); // ctnProcedureHead ReadNextAtom; if not AtomIsIdentifier(false) then exit; ReadNextAtom; if (CurPos.Flag<>cafPoint) then exit; Result:=true; exit; end; end; function TPascalReaderTool.NodeIsFunction(ProcNode: TCodeTreeNode): boolean; begin Result:=false; if (ProcNode=nil) or (ProcNode.Desc<>ctnProcedure) then exit; MoveCursorToNodeStart(ProcNode); ReadNextAtom; if UpAtomIs('CLASS') then ReadNextAtom; Result:=UpAtomIs('FUNCTION'); end; function TPascalReaderTool.NodeIsConstructor(ProcNode: TCodeTreeNode): boolean; begin Result:=false; if (ProcNode=nil) then exit; if ProcNode.Desc=ctnProcedureHead then ProcNode:=ProcNode.Parent; if ProcNode.Desc<>ctnProcedure then exit; MoveCursorToNodeStart(ProcNode); ReadNextAtom; Result:=UpAtomIs('CONSTRUCTOR'); end; function TPascalReaderTool.NodeIsDestructor(ProcNode: TCodeTreeNode): boolean; begin Result:=false; if (ProcNode=nil) then exit; if ProcNode.Desc=ctnProcedureHead then ProcNode:=ProcNode.Parent; if ProcNode.Desc<>ctnProcedure then exit; MoveCursorToNodeStart(ProcNode); ReadNextAtom; Result:=UpAtomIs('DESTRUCTOR'); end; function TPascalReaderTool.NodeIsForwardProc(ProcNode: TCodeTreeNode): boolean; begin Result:=false; // check if procedure if (ProcNode=nil) or (ProcNode.Desc<>ctnProcedure) then exit; // check if in interface if (ProcNode.Parent<>nil) and (ProcNode.Parent.Desc=ctnInterface) then exit(true); // check if has forward if (ctnsForwardDeclaration and ProcNode.SubDesc)>0 then exit(true); end; function TPascalReaderTool.NodeIsOperator(ProcNode: TCodeTreeNode): boolean; begin Result:=false; if (ProcNode=nil) then exit; if ProcNode.Desc=ctnProcedureHead then ProcNode:=ProcNode.Parent; if ProcNode.Desc<>ctnProcedure then exit; Result:=CompareIdentifiers('operator',@Src[ProcNode.StartPos])=0; end; function TPascalReaderTool.NodeIsResultIdentifier(Node: TCodeTreeNode ): boolean; begin Result:=(Node<>nil) and (Node.Desc=ctnVarDefinition) and (Node.Parent<>nil) and (Node.Parent.Desc=ctnProcedureHead); end; function TPascalReaderTool.NodeIsResultType(Node: TCodeTreeNode): boolean; begin Result:=(Node<>nil) and (Node.Desc=ctnIdentifier) and (Node.Parent<>nil) and (Node.Parent.Desc=ctnProcedureHead); end; function TPascalReaderTool.NodeIsPartOfTypeDefinition(ANode: TCodeTreeNode ): boolean; begin ANode:=ANode.Parent; while ANode<>nil do begin if ANode.Desc in (AllIdentifierDefinitions+AllPascalTypes) then begin Result:=true; exit; end; ANode:=ANode.Parent; end; Result:=false; end; function TPascalReaderTool.ExtractDefinitionNodeType( DefinitionNode: TCodeTreeNode): string; var TypeNode: TCodeTreeNode; begin Result:=''; TypeNode:=FindTypeNodeOfDefinition(DefinitionNode); if TypeNode=nil then exit; if TypeNode.Desc=ctnIdentifier then Result:=GetIdentifier(@Src[TypeNode.StartPos]); end; function TPascalReaderTool.ExtractDefinitionName(DefinitionNode: TCodeTreeNode ): string; begin if DefinitionNode.Desc=ctnGenericType then begin if DefinitionNode.FirstChild<>nil then Result:=GetIdentifier(@Src[DefinitionNode.FirstChild.StartPos]) else Result:=''; end else begin Result:=GetIdentifier(@Src[DefinitionNode.StartPos]); end; end; function TPascalReaderTool.PositionInDefinitionName( DefinitionNode: TCodeTreeNode; CleanPos: integer): boolean; var StartPos: LongInt; begin if DefinitionNode.Desc=ctnGenericType then begin if DefinitionNode.FirstChild<>nil then StartPos:=DefinitionNode.FirstChild.StartPos else StartPos:=0; end else begin StartPos:=DefinitionNode.StartPos; end; Result:=(CleanPos>=StartPos) and (CleanPosctnProcedureHead) then exit; // find first variable node of this type (e.g. var a,b,c,d: integer) while (DefinitionNode.PriorBrother<>nil) and (DefinitionNode.PriorBrother.FirstChild=nil) do DefinitionNode:=DefinitionNode.PriorBrother; if DefinitionNode.PriorBrother<>nil then MoveCursorToCleanPos(DefinitionNode.PriorBrother.EndPos) else MoveCursorToCleanPos(DefinitionNode.Parent.StartPos); ReadNextAtom; while (CurPos.StartPosctnTypeDefinition) or (TypeNode.FirstChild=nil) then exit; if (TypeNode.FirstChild.Desc in AllClasses) and (TypeNode.FirstChild.SubDesc and ctnsForwardDeclaration=0) then Result:=true; end; function TPascalReaderTool.NodeIsForwardType(TypeNode: TCodeTreeNode): boolean; begin Result:=false; if (TypeNode=nil) or (TypeNode.Desc<>ctnTypeDefinition) or (TypeNode.FirstChild=nil) then exit; if (TypeNode.FirstChild.Desc in AllClasses) and (TypeNode.FirstChild.SubDesc and ctnsForwardDeclaration>0) then Result:=true; end; function TPascalReaderTool.FindForwardTypeNode(TypeNode: TCodeTreeNode; SearchFirst: boolean): TCodeTreeNode; { Find the first forward type of TypeNode } function Next: TCodeTreeNode; begin Result:=FindForwardTypeNode; if Result.PriorBrother<>nil then // search upwards Result:=Result.PriorBrother else if Result.Parent.Desc in AllDefinitionSections then begin // type section was searched // check for other type sections in front Result:=Result.Parent; repeat while (Result.PriorBrother<>nil) do begin Result:=Result.PriorBrother; if (Result.Desc in AllDefinitionSections) and (Result.LastChild<>nil) then begin Result:=Result.LastChild; exit; end; end; // check if in implementation section if (Result.Parent=nil) or (Result.Parent.Desc<>ctnImplementation) then exit(nil); Result:=Result.Parent; // check if there is an interface section if (Result.PriorBrother=nil) or (Result.PriorBrother.Desc<>ctnInterface) then exit(nil); // search in interface section Result:=Result.PriorBrother; Result:=Result.LastChild; until Result=nil; end else exit; end; var Node: TCodeTreeNode; begin Result:=nil; if not NodeCanHaveForwardType(TypeNode) then exit; Node:=TypeNode; while Node<>nil do begin if Node.Desc in AllIdentifierDefinitions then begin if CompareIdentifiers(@Src[TypeNode.StartPos],@Src[Node.StartPos])=0 then begin if (Node.Desc=ctnTypeDefinition) and NodeIsForwardType(Node) then begin // a forward Result:=Node; if not SearchFirst then exit; end else begin // a redefinition exit; end; end; end; Node:=Next; end; end; function TPascalReaderTool.FindTypeOfForwardNode(TypeNode: TCodeTreeNode ): TCodeTreeNode; function Next: TCodeTreeNode; begin Result:=FindTypeOfForwardNode; if Result.NextBrother<>nil then // search forwards Result:=Result.NextBrother else if Result.Parent.Desc in AllDefinitionSections then begin // type section was searched // check for other type sections in front Result:=Result.Parent; repeat while (Result.NextBrother<>nil) do begin Result:=Result.NextBrother; if (Result.Desc in AllDefinitionSections) and (Result.FirstChild<>nil) then begin Result:=Result.FirstChild; exit; end; end; // check if in interface section if (Result.Parent=nil) or (Result.Parent.Desc<>ctnInterface) then exit(nil); Result:=Result.Parent; // check if there is an implementation section if (Result.NextBrother=nil) or (Result.NextBrother.Desc<>ctnImplementation) then exit(nil); // search in implementation section Result:=Result.NextBrother; Result:=Result.FirstChild; until Result=nil; end else exit; end; var Node: TCodeTreeNode; begin Result:=nil; if not NodeIsForwardType(TypeNode) then exit; Node:=TypeNode; while Node<>nil do begin if Node.Desc in AllIdentifierDefinitions then begin if CompareIdentifiers(@Src[TypeNode.StartPos],@Src[Node.StartPos])=0 then begin if (Node.Desc=ctnTypeDefinition) and (not NodeIsForwardType(Node)) then begin // a type Result:=Node; exit; end else begin // a redefinition exit; end; end; end; Node:=Next; end; end; function TPascalReaderTool.ExtractArrayRange(ArrayNode: TCodeTreeNode; Attr: TProcHeadAttributes): string; begin Result:=''; if (ArrayNode=nil) or (ArrayNode.Desc<>ctnRangedArrayType) then exit; MoveCursorToNodeStart(ArrayNode); if not ReadNextUpAtomIs('ARRAY') then exit; if not ReadNextAtomIsChar('[') then exit; Result:=ExtractBrackets(CurPos.StartPos,Attr); end; function TPascalReaderTool.GetSourceName(DoBuildTree: boolean): string; var NamePos: TAtomPosition; begin Result:=''; if DoBuildTree then BuildTree(true); if not GetSourceNamePos(NamePos) then exit; CachedSourceName:=copy(Src,NamePos.StartPos,NamePos.EndPos-NamePos.StartPos); Result:=CachedSourceName; end; function TPascalReaderTool.PropertyIsDefault(PropertyNode: TCodeTreeNode ): boolean; begin Result:=false; if (PropertyNode=nil) or (PropertyNode.Desc<>ctnProperty) then exit; MoveCursorToCleanPos(PropertyNode.EndPos); ReadPriorAtom; if (CurPos.Flag<>cafSemicolon) then exit; ReadPriorAtom; Result:=UpAtomIs('DEFAULT'); end; function TPascalReaderTool.PropertyNodeHasParamList(PropNode: TCodeTreeNode ): boolean; begin // ToDo: ppu, dcu Result:=false; if not MoveCursorToPropName(PropNode) then exit; ReadNextAtom; Result:=(CurPos.Flag=cafEdgedBracketOpen); end; function TPascalReaderTool.PropNodeIsTypeLess(PropNode: TCodeTreeNode ): boolean; begin // ToDo: ppu, dcu Result:=false; if not MoveCursorToPropName(PropNode) then exit; ReadNextAtom; // read colon, skip parameters if CurPos.Flag=cafEdgedBracketOpen then begin ReadTilBracketClose(true); ReadNextAtom; end; Result:=(CurPos.Flag<>cafColon); end; function TPascalReaderTool.PropertyHasSpecifier(PropNode: TCodeTreeNode; const s: string; ExceptionOnNotFound: boolean): boolean; begin // ToDo: ppu, dcu Result:=false; if not MoveCursorToPropName(PropNode) then exit; if not AtomIsIdentifier(ExceptionOnNotFound) then exit; ReadNextAtom; if CurPos.Flag=cafEdgedBracketOpen then begin if not ReadTilBracketClose(ExceptionOnNotFound) then exit; ReadNextAtom; end; if CurPos.Flag=cafColon then begin // read type ReadNextAtom; if not AtomIsIdentifier(ExceptionOnNotFound) then exit; ReadNextAtom; if CurPos.Flag=cafPoint then begin ReadNextAtom; if not AtomIsIdentifier(ExceptionOnNotFound) then exit; ReadNextAtom; end; end; // read specifiers while not (CurPos.Flag in [cafSemicolon,cafNone]) do begin if WordIsPropertySpecifier.DoItCaseInsensitive(@Src[CurPos.StartPos]) then begin if AtomIs(s) then exit(true); end else if CurPos.Flag=cafEdgedBracketOpen then begin if not ReadTilBracketClose(ExceptionOnNotFound) then exit; ReadNextAtom; end; ReadNextAtom; end; // read modifiers while CurPos.Flag=cafSemicolon do begin ReadNextAtom; if UpAtomIs('DEFAULT') or UpAtomIs('NODEFAULT') or UpAtomIs('DEPRECATED') then begin if CompareIdentifierPtrs(@Src[CurPos.StartPos],Pointer(s))=0 then exit(true); end else if UpAtomIs('ENUMERATOR') then begin if CompareIdentifierPtrs(@Src[CurPos.StartPos],Pointer(s))=0 then exit(true); ReadNextAtom; if not AtomIsIdentifier(false) then exit; end else exit; ReadNextAtom; end; end; function TPascalReaderTool.ProcNodeHasParamList(ProcNode: TCodeTreeNode ): boolean; begin // ToDo: ppu, dcu Result:=false; if ProcNode.Desc=ctnProcedure then ProcNode:=ProcNode.FirstChild; MoveCursorToNodeStart(ProcNode); ReadNextAtom; // read name ReadNextAtom; if AtomIsChar('.') then begin ReadNextAtom; ReadNextAtom; end; Result:=AtomIsChar('('); end; procedure TPascalReaderTool.MoveCursorToUsesStart(UsesNode: TCodeTreeNode); begin if (UsesNode=nil) or ((UsesNode.Desc<>ctnUsesSection) and (UsesNode.Desc<>ctnContainsSection)) then RaiseException('[TPascalParserTool.MoveCursorToUsesStart] ' +'internal error: invalid UsesNode'); // search through the uses section MoveCursorToCleanPos(UsesNode.StartPos); ReadNextAtom; if (not UpAtomIs('USES')) and (not UpAtomIs('CONTAINS')) then RaiseExceptionFmt(ctsStrExpectedButAtomFound,['uses',GetAtom]); ReadNextAtom; end; procedure TPascalReaderTool.MoveCursorToUsesEnd(UsesNode: TCodeTreeNode); begin if (UsesNode=nil) or ((UsesNode.Desc<>ctnUsesSection) and (UsesNode.Desc<>ctnContainsSection)) then RaiseException('[TPascalParserTool.MoveCursorToUsesEnd] ' +'internal error: invalid UsesNode'); // search backwards through the uses section MoveCursorToCleanPos(UsesNode.EndPos); ReadPriorAtom; // read ';' if not AtomIsChar(';') then RaiseExceptionFmt(ctsStrExpectedButAtomFound,[';',GetAtom]); end; procedure TPascalReaderTool.ReadNextUsedUnit(out UnitNameAtom, InAtom: TAtomPosition); begin AtomIsIdentifier(true); UnitNameAtom:=CurPos; ReadNextAtom; if UpAtomIs('IN') then begin ReadNextAtom; // read filename if not AtomIsStringConstant then RaiseExceptionFmt(ctsStrExpectedButAtomFound,[ctsStringConstant,GetAtom]); InAtom:=CurPos; ReadNextAtom; // read comma or semicolon end else begin InAtom:=CleanAtomPosition; end; end; procedure TPascalReaderTool.ReadPriorUsedUnit(out UnitNameAtom, InAtom: TAtomPosition); begin ReadPriorAtom; // read unitname if AtomIsStringConstant then begin InAtom:=CurPos; ReadPriorAtom; // read 'in' if not UpAtomIs('IN') then RaiseExceptionFmt(ctsStrExpectedButAtomFound,[ctsKeywordIn,GetAtom]); ReadPriorAtom; // read unitname end else begin InAtom:=CleanAtomPosition; end; AtomIsIdentifier(true); UnitNameAtom:=CurPos; end; function TPascalReaderTool.FindCommentInFront(const StartPos: TCodeXYPosition; const CommentText: string; InvokeBuildTree, SearchInParentNode, WithCommentBounds, CaseSensitive, IgnoreSpaces, CompareOnlyStart: boolean; out CommentStart, CommentEnd: TCodeXYPosition): boolean; var CleanCursorPos: integer; CommentCleanStart: integer; CommentCleanEnd: integer; begin Result:=false; if CommentText='' then exit; {debugln('TPascalReaderTool.FindCommentInFront A CommentText="',CommentText,'" ', ' StartPos=Y='+dbgs(StartPos.Y)+',X='+dbgs(StartPos.X), ' InvokeBuildTree='+dbgs(InvokeBuildTree), ' SearchInParentNode='+dbgs(SearchInParentNode), ' WithCommentBounds='+dbgs(WithCommentBounds), ' CaseSensitive='+dbgs(CaseSensitive), ' IgnoreSpaces='+dbgs(IgnoreSpaces), ' CompareOnlyStart='+dbgs(CompareOnlyStart)); } // parse source and find clean positions if InvokeBuildTree then BuildTreeAndGetCleanPos(trAll,StartPos,CleanCursorPos,[]) else if CaretToCleanPos(StartPos,CleanCursorPos)<>0 then exit; Result:=FindCommentInFront(CleanCursorPos,CommentText,SearchInParentNode, WithCommentBounds,CaseSensitive,IgnoreSpaces,CompareOnlyStart, CommentCleanStart,CommentCleanEnd); if not Result then exit; Result:=(CommentCleanStart>=1) and CleanPosToCaret(CommentCleanStart,CommentStart) and CleanPosToCaret(CommentCleanEnd,CommentEnd); end; function TPascalReaderTool.FindCommentInFront(const StartPos: integer; const CommentText: string; SearchInParentNode, WithCommentBounds, CaseSensitive, IgnoreSpaces, CompareOnlyStart: boolean; out CommentStart, CommentEnd: integer): boolean; // searches a comment in front. var FoundStartPos: integer; FoundEndPos: integer; procedure CompareComment(CStartPos, CEndPos: integer); var Found: LongInt; CompareStartPos: LongInt; CompareEndPos: LongInt; CompareLen: Integer; CompareCommentLength: Integer; begin //debugln('CompareComment "',copy(Src,CStartPos,CEndPos-CStartPos),'"'); CompareStartPos:=CStartPos; CompareEndPos:=CEndPos; if not WithCommentBounds then begin // chomp comment boundaries case Src[CompareStartPos] of '/','(': inc(CompareStartPos,2); '{': inc(CompareStartPos,1); end; case Src[CompareEndPos-1] of '}': dec(CompareEndPos); ')': dec(CompareEndPos,2); #10,#13: begin dec(CompareEndPos); if (Src[CompareEndPos-1] in [#10,#13]) and (Src[CompareEndPos-1]<>Src[CompareEndPos]) then dec(CompareEndPos); end; end; end; if IgnoreSpaces then begin while (CompareStartPos<=CompareEndPos) and IsSpaceChar[Src[CompareStartPos]] do inc(CompareStartPos); end; CompareCommentLength:=length(CommentText); CompareLen:=CompareEndPos-CompareStartPos; if CompareOnlyStart and (CompareLen>CompareCommentLength) then CompareLen:=CompareCommentLength; //debugln('Compare: "',copy(Src,CompareStartPos,CompareEndPos-CompareStartPos),'"', // ' "',CommentText,'"'); if IgnoreSpaces then begin Found:=CompareTextIgnoringSpace( @Src[CompareStartPos],CompareLen, @CommentText[1],length(CommentText), CaseSensitive); end else begin Found:=CompareText(@Src[CompareStartPos],CompareLen, @CommentText[1],length(CommentText), CaseSensitive); end; if Found=0 then begin FoundStartPos:=CStartPos; FoundEndPos:=CEndPos; end; end; var ANode: TCodeTreeNode; p: LongInt; CommentLvl: Integer; CommentStartPos: LongInt; begin Result:=false; if CommentText='' then exit; {debugln('TPascalReaderTool.FindCommentInFront A CommentText="',CommentText,'" ', ' StartPos=Y='+dbgs(StartPos.Y)+',X='+dbgs(StartPos.X), ' InvokeBuildTree='+dbgs(InvokeBuildTree), ' SearchInParentNode='+dbgs(SearchInParentNode), ' WithCommentBounds='+dbgs(WithCommentBounds), ' CaseSensitive='+dbgs(CaseSensitive), ' IgnoreSpaces='+dbgs(IgnoreSpaces), ' CompareOnlyStart='+dbgs(CompareOnlyStart)); } // find node ANode:=FindDeepestNodeAtPos(StartPos,true); if (ANode=nil) then exit; { find end of last atom in front of node for example: uses classes; // Comment type If ANode is the 'type' block, the position after the semicolon is searched } if SearchInParentNode and (ANode.Parent<>nil) then begin // search all siblings in front ANode:=ANode.Parent; MoveCursorToCleanPos(ANode.Parent.StartPos); end else if ANode.PriorBrother<>nil then begin // search between prior sibling and this node //DebugLn('TPascalReaderTool.FindCommentInFront ANode.Prior=',ANode.Prior.DescAsString); MoveCursorToLastNodeAtom(ANode.PriorBrother); end else if ANode.Parent<>nil then begin // search from start of parent node to this node //DebugLn('TPascalReaderTool.FindCommentInFront ANode.Parent=',ANode.Parent.DescAsString); MoveCursorToCleanPos(ANode.Parent.StartPos); end else begin // search in this node //DebugLn('TPascalReaderTool.FindCommentInFront Aode=',ANode.DescAsString); MoveCursorToCleanPos(ANode.StartPos); end; //debugln('TPascalReaderTool.FindCommentInFront B Area="',copy(Src,CurPos.StartPos,StartPos-CurPos.StartPos),'"'); FoundStartPos:=-1; repeat p:=CurPos.EndPos; //debugln('TPascalReaderTool.FindCommentInFront Atom=',GetAtom); // read space and comment till next atom CommentLvl:=0; while true do begin case Src[p] of #0: if p>SrcLen then break else inc(p); #1..#32: inc(p); '{': // pascal comment begin CommentLvl:=1; CommentStartPos:=p; inc(p); while true do begin case Src[p] of #0: if p>SrcLen then break; '{': if Scanner.NestedComments then inc(CommentLvl); '}': begin dec(CommentLvl); if CommentLvl=0 then break; end; end; inc(p); end; inc(p); CompareComment(CommentStartPos,p); end; '/': // Delphi comment if (Src[p+1]<>'/') then begin break; end else begin CommentStartPos:=p; inc(p,2); while (not (Src[p] in [#10,#13,#0])) do inc(p); inc(p); if (p<=SrcLen) and (Src[p] in [#10,#13]) and (Src[p-1]<>Src[p]) then inc(p); CompareComment(CommentStartPos,p); end; '(': // old turbo pascal comment if (Src[p+1]<>'*') then begin break; end else begin CommentStartPos:=p; inc(p,3); while (p<=SrcLen) and ((Src[p-1]<>'*') or (Src[p]<>')')) do inc(p); inc(p); CompareComment(CommentStartPos,p); end; else break; end; end; ReadNextAtom; //DebugLn('TPascalReaderTool.FindCommentInFront NextAtom=',GetAtom); until (CurPos.StartPos>=StartPos) or (CurPos.EndPos>=SrcLen); Result:=(FoundStartPos>=1); CommentStart:=FoundStartPos; CommentEnd:=FoundEndPos; end; function TPascalReaderTool.CommentCode(const StartPos, EndPos: integer; SourceChangeCache: TSourceChangeCache; Apply: boolean): boolean; var i: LongInt; CurStartPos: LongInt; CommentNeeded: Boolean; CurEndPos: LongInt; begin if StartPos>=EndPos then RaiseException('TStandardCodeTool CommentCode'); Result:=false; // comment with curly brackets {} i:=StartPos; CurStartPos:=i; CurEndPos:=CurStartPos; CommentNeeded:=false; repeat //debugln(['TPascalReaderTool.CommentCode ',dbgstr(Src[i]),' Needed=',CommentNeeded,' ',dbgstr(copy(Src,CurStartPos,CurEndPos-CurStartPos))]); if (Src[i]='{') or (i>=EndPos) then begin // the area contains a comment -> comment in front if CommentNeeded then begin if not SourceChangeCache.Replace(gtNone,gtNone, CurStartPos,CurStartPos,'{') then exit; if not SourceChangeCache.Replace(gtNone,gtNone, CurEndPos,CurEndPos,'}') then exit; //DebugLn('Comment "',copy(Src,CurStartPos,i-CurStartPos),'"'); CommentNeeded:=false; end; if i>=EndPos then break; // skip comment i:=FindCommentEnd(Src,i,Scanner.NestedComments)-1; end else if not IsSpaceChar[Src[i]] then begin if not CommentNeeded then begin CurStartPos:=i; CommentNeeded:=true; end; CurEndPos:=i+1; end; inc(i); until false; if Apply then Result:=SourceChangeCache.Apply else Result:=true; end; function TPascalReaderTool.GetPasDocComments(const StartPos: TCodeXYPosition; InvokeBuildTree: boolean; out ListOfPCodeXYPosition: TFPList): boolean; // Comments are normally in front. // { Description of TMyClass. } // TMyClass = class // // Comments can be behind in the same line // property Color; // description of Color // // Comments can be in the following line if started with < function CommentBelongsToPrior(CommentStart: integer): boolean; var p: Integer; begin //DebugLn(['CommentBelongsToPrior Comment=',dbgstr(copy(Src,CommentStart,20))]); if (CommentStart=1) and (Src[p] in [' ',#9]) do dec(p); //DebugLn(['CommentBelongsToPrior Code in front: ',dbgstr(copy(Src,p,20))]); if (p<1) or (Src[p] in [#10,#13]) then Result:=false else Result:=true; // there is code in the same line in front of the comment end; end; procedure Add(CleanPos: integer); var CodePos: TCodeXYPosition; begin if not CleanPosToCaret(CleanPos,CodePos) then exit; AddCodePosition(ListOfPCodeXYPosition,CodePos); end; function Scan(StartPos, EndPos: integer): boolean; var p: LongInt; begin // read comments (start in front of node) //DebugLn(['TPascalReaderTool.GetPasDocComments Scan Src=',copy(Src,StartPos,EndPos-StartPos)]); p:=FindLineEndOrCodeInFrontOfPosition(StartPos,true); while p=EndPos) or ((Src[p]='{') and (Src[p+1]='$')) or ((Src[p]='(') and (Src[p+1]='*') and (Src[p+2]='$')) then break; //debugln(['TStandardCodeTool.GetPasDocComments Comment="',copy(Src,p,FindCommentEnd(Src,p,Scanner.NestedComments)-p),'"']); if (p0 then exit; // find node ANode:=FindDeepestNodeAtPos(CleanCursorPos,true); if (ANode=nil) then exit; if (ANode.Desc=ctnProcedureHead) and (ANode.Parent<>nil) and (ANode.Parent.Desc=ctnProcedure) then ANode:=ANode.Parent; // add space behind node to scan range NextNode:=ANode.Next; if NextNode<>nil then EndPos:=NextNode.StartPos else EndPos:=ANode.EndPos; // scan range for comments if not Scan(ANode.StartPos,EndPos) then exit; if ANode.Desc in AllIdentifierDefinitions then begin // scan behind type // for example: i: integer; // comment TypeNode:=FindTypeNodeOfDefinition(ANode); if TypeNode<>nil then begin NextNode:=TypeNode.Next; if NextNode<>nil then EndPos:=NextNode.StartPos else EndPos:=ANode.EndPos; if not Scan(TypeNode.EndPos,EndPos) then exit; end; end; Result:=true; end; procedure TPascalReaderTool.CalcMemSize(Stats: TCTMemStats); begin inherited CalcMemSize(Stats); Stats.Add('TPascalReaderTool',MemSizeString(CachedSourceName)); end; end.