lazarus/components/codetools/pascalreadertool.pas
mattias b1acac9beb codetools: clean up
git-svn-id: trunk@48661 -
2015-04-06 17:39:43 +00:00

3493 lines
111 KiB
ObjectPascal

{
***************************************************************************
* *
* 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 <http://www.gnu.org/copyleft/gpl.html>. 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, CodeCache,
CodeAtom, CustomCodeTool, PascalParserTool, KeywordFuncLists, BasicCodeTools,
LinkScanner, AVL_Tree;
type
TPascalHintModifier = (
phmDeprecated,
phmPlatform,
phmLibrary,
phmUnimplemented,
phmExperimental
);
TPascalHintModifiers = set of TPascalHintModifier;
TEPRIRange = (
epriInCode,
epriInComment,
epriInDirective
);
TOnEachPRIdentifier = procedure(Sender: TPascalParserTool;
IdentifierCleanPos: integer; Range: TEPRIRange;
Node: TCodeTreeNode; Data: Pointer; var Abort: boolean) of object;
{ TPascalReaderTool }
TPascalReaderTool = class(TPascalParserTool)
protected
CachedSourceName: string;
procedure RaiseStrConstExpected;
public
// comments
function CleanPosIsInComment(CleanPos, CleanCodePosInFront: integer;
out CommentStart, CommentEnd: integer;
OuterCommentBounds: boolean = true): 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 ExtractIdentifierWithPoints(StartPos: integer;
ExceptionOnError: boolean): string;
function ExtractIdentCharsFromStringConstant(
StartPos, MinPos, MaxPos, MaxLen: integer): string;
function ReadStringConstantValue(StartPos: integer): string;
function GetNodeIdentifier(Node: TCodeTreeNode): PChar;
function GetHintModifiers(Node: TCodeTreeNode): TPascalHintModifiers;
procedure ForEachIdentifierInCleanSrc(StartPos, EndPos: integer;
SkipComments: boolean; Node: TCodeTreeNode;
const OnIdentifier: TOnEachPRIdentifier; Data: pointer;
var Abort: boolean); // range in clean source
procedure ForEachIdentifierInNode(Node: TCodeTreeNode; SkipComments: boolean;
const OnIdentifier: TOnEachPRIdentifier; Data: Pointer; var Abort: boolean); // node and child nodes
procedure ForEachIdentifier(SkipComments: boolean;
const OnIdentifier: TOnEachPRIdentifier; Data: Pointer); // whole unit/program
// properties
function ExtractPropType(PropNode: TCodeTreeNode;
InUpperCase, EmptyIfIndexed: boolean): string;
function MoveCursorToPropType(PropNode: TCodeTreeNode): boolean;
function MoveCursorToPropName(PropNode: TCodeTreeNode): boolean;
procedure MoveCursorBehindPropName(PropNode: TCodeTreeNode);
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;
UpperKeyword: string; ExceptionOnNotFound: boolean = true): boolean;
// procs
function ExtractProcName(ProcNode: TCodeTreeNode;
Attr: TProcHeadAttributes): string;
function ExtractProcHead(ProcNode: TCodeTreeNode;
Attr: TProcHeadAttributes): string;
function ExtractProcedureHeader(CursorPos: TCodeXYPosition;
Attributes: TProcHeadAttributes; var ProcHead: string): boolean;
function ExtractClassNameOfProcNode(ProcNode: TCodeTreeNode;
AddParentClasses: boolean = true): 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 = [phpWithoutClassKeyword,phpWithoutClassName]
): 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);
procedure MoveCursorBehindProcName(ProcNode: TCodeTreeNode);
function PositionInProcName(ProcNode: TCodeTreeNode;
SkipClassName: boolean; CleanPos: integer): boolean;
function PositionInFuncResultName(ProcNode: TCodeTreeNode;
CleanPos: integer): boolean;
function ProcNodeHasParamList(ProcNode: TCodeTreeNode): boolean;
function ProcNodeHasOfObject(ProcNode: TCodeTreeNode): boolean;
function GetProcParamList(ProcNode: TCodeTreeNode;
Parse: boolean = true): TCodeTreeNode;
function NodeIsInAMethod(Node: TCodeTreeNode): boolean;
function NodeIsMethodBody(ProcNode: TCodeTreeNode): boolean;
function GetMethodOfBody(Node: TCodeTreeNode): TCodeTreeNode;
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(Node: TCodeTreeNode;
InUpperCase: boolean; WithParents: boolean = true): string;
function ExtractClassPath(Node: TCodeTreeNode): string;
function ExtractClassInheritance(ClassNode: TCodeTreeNode;
Attr: TProcHeadAttributes): string;
function FindClassNode(StartNode: TCodeTreeNode;
const AClassName: string; // nested: A.B
IgnoreForwards, IgnoreNonForwards: boolean): TCodeTreeNode;
function FindClassNodeBackwards(StartNode: TCodeTreeNode;
const AClassName: string;
IgnoreForwards, IgnoreNonForwards: boolean): TCodeTreeNode;
function FindNestedClass(RootClassNode: TCodeTreeNode;
AClassName: PChar; SkipFirst: 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 GetClassVisibility(Node: TCodeTreeNode): TCodeTreeNodeDesc;
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 FindLastIdentNodeInClass(ClassNode: TCodeTreeNode): TCodeTreeNode;
function FindNextIdentNodeInClass(Node: TCodeTreeNode): TCodeTreeNode;
function FindPriorIdentNodeInClass(Node: 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 GetFirstGroupVarNode(VarNode: TCodeTreeNode): TCodeTreeNode;
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;
// module sections
function ExtractSourceName: string;
function GetSourceNamePos(out NamePos: TAtomPosition): boolean;
function GetSourceName(DoBuildTree: boolean = true): string;
function GetSourceType: TCodeTreeNodeDesc;
function PositionInSourceName(CleanPos: integer): boolean;
// uses sections
procedure MoveCursorToUsesStart(UsesNode: TCodeTreeNode);
procedure MoveCursorToUsesEnd(UsesNode: TCodeTreeNode);
function ReadNextUsedUnit(out UnitNameRange, InAtom: TAtomPosition;
SyntaxExceptions: boolean = true): boolean;
procedure ReadPriorUsedUnit(out UnitNameRange, InAtom: TAtomPosition);
function ExtractUsedUnitNameAtCursor(InFilename: PAnsiString = nil): string;
function ExtractUsedUnitName(UseUnitNode: TCodeTreeNode;
InFilename: PAnsiString = nil): string;
function ReadAndCompareUsedUnit(const AnUnitName: string): boolean;
// comments
function FindCommentInFront(const StartPos: TCodeXYPosition;
const CommentText: string; InvokeBuildTree, SearchInParentNode,
WithCommentBounds, CaseSensitive, IgnoreSpaces,
CompareOnlyStart: boolean;
out CommentStart, CommentEnd: TCodeXYPosition): boolean;
function FindCommentInFront(StartPos: integer;
const CommentText: string; SearchInParentNode,
WithCommentBounds, CaseSensitive, IgnoreSpaces,
CompareOnlyStart: boolean;
out CommentStart, CommentEnd: integer): boolean;
function GetPasDocComments(const StartPos: TCodeXYPosition;
InvokeBuildTree: boolean;
out ListOfPCodeXYPosition: TFPList): boolean;
function GetPasDocComments(Node: TCodeTreeNode;
out ListOfPCodeXYPosition: TFPList): boolean;
procedure CalcMemSize(Stats: TCTMemStats); override;
end;
implementation
{ TPascalReaderTool }
procedure TPascalReaderTool.RaiseStrConstExpected;
begin
RaiseExceptionFmt(ctsStrExpectedButAtomFound,[ctsStringConstant,GetAtom]);
end;
function TPascalReaderTool.CleanPosIsInComment(CleanPos,
CleanCodePosInFront: integer; out CommentStart, CommentEnd: integer;
OuterCommentBounds: boolean): boolean;
var CommentLvl, CurCommentPos: integer;
CurEnd: Integer;
CurCommentInnerEnd: Integer;
begin
Result:=false;
CommentStart:=0;
CommentEnd:=0;
if CleanPos>SrcLen then exit;
if CleanCodePosInFront>CleanPos then
RaiseException(
'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
if LastAtoms.Count>0 then
CommentStart:=LastAtoms.GetValueAt(0).EndPos
else
CommentStart:=CleanCodePosInFront;
CurEnd:=CurPos.StartPos;
if CurEnd>SrcLen then CurEnd:=SrcLen+1;
while CommentStart<CurEnd do begin
if IsCommentStartChar[Src[CommentStart]] then begin
CurCommentPos:=CommentStart;
CurCommentInnerEnd:=CurEnd;
case Src[CommentStart] of
'{':
begin
inc(CurCommentPos);
if (CurCommentPos<=SrcLen) and (Src[CurCommentPos]=#3) then begin
// codetools skip comment
inc(CurCommentPos);
if not OuterCommentBounds then CommentStart:=CurCommentPos;
while (CurCommentPos<CurEnd) do begin
if (Src[CurCommentPos]=#3)
and (CurCommentPos+1<CurEnd) and (Src[CurCommentPos+1]='}')
then begin
CurCommentInnerEnd:=CurCommentPos;
inc(CurCommentPos,2);
break;
end;
inc(CurCommentPos);
end;
end else begin
// pascal comment
if not OuterCommentBounds then CommentStart:=CurCommentPos;
CommentLvl:=1;
while (CurCommentPos<CurEnd) do begin
case Src[CurCommentPos] of
'{': if Scanner.NestedComments then inc(CommentLvl);
'}':
begin
dec(CommentLvl);
if (CommentLvl=0) then begin
CurCommentInnerEnd:=CurCommentPos;
inc(CurCommentPos);
break;
end;
end;
end;
inc(CurCommentPos);
end;
end;
end;
'/': // Delphi comment
if (CurCommentPos<SrcLen) and (Src[CurCommentPos+1]='/') then
begin
inc(CurCommentPos,2);
if not OuterCommentBounds then CommentStart:=CurCommentPos;
while (CurCommentPos<CurEnd)
and (not (Src[CurCommentPos] in [#10,#13])) do
inc(CurCommentPos);
CurCommentInnerEnd:=CurCommentPos;
inc(CurCommentPos);
if (CurCommentPos<CurEnd)
and (Src[CurCommentPos] in [#10,#13])
and (Src[CurCommentPos-1]<>Src[CurCommentPos]) then
inc(CurCommentPos);
end else
break;
'(': // Turbo pascal comment
if (CurCommentPos<SrcLen) and (Src[CurCommentPos+1]='*') then
begin
inc(CurCommentPos,2);
if not OuterCommentBounds then CommentStart:=CurCommentPos;
while (CurCommentPos<CurEnd) do begin
if (Src[CurCommentPos]='*') and (CurCommentPos+1<CurEnd)
and (Src[CurCommentPos+1]=')') then
begin
CurCommentInnerEnd:=CurCommentPos;
inc(CurCommentPos,2);
break;
end;
inc(CurCommentPos);
end;
end else
break;
end;
if (CurCommentPos>CommentStart) and (CleanPos<CurCommentPos) then
begin
// CleanPos in comment
if OuterCommentBounds then
CommentEnd:=CurCommentPos
else
CommentEnd:=CurCommentInnerEnd;
exit(true);
end;
CommentStart:=CurCommentPos;
end else if IsSpaceChar[Src[CommentStart]] then begin
repeat
inc(CommentStart);
until (CommentStart>=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;
AtomIsIdentifierE;
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;
AtomIsIdentifierE;
if InUpperCase then
Result:=GetUpAtom
else
Result:=GetAtom;
end;
function TPascalReaderTool.ExtractProcName(ProcNode: TCodeTreeNode;
Attr: TProcHeadAttributes): string;
var
ProcHeadNode: TCodeTreeNode;
Part: String;
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);
repeat
ReadNextAtom;
if not AtomIsIdentifier then exit;
if phpInUpperCase in Attr then
Part:=GetUpAtom
else
Part:=GetAtom;
ReadNextAtom;
if (CurPos.Flag<>cafPoint) then begin
// end of method identifier is the proc name
if phpWithoutName in Attr then exit;
if Result<>'' then Result:=Result+'.';
Result:=Result+Part;
exit;
end;
if not (phpWithoutClassName in Attr) then begin
// in front of . is class name
if Result<>'' then Result:=Result+'.';
Result:=Result+Part;
end;
until false;
end;
function TPascalReaderTool.ExtractProcHead(ProcNode: TCodeTreeNode;
Attr: TProcHeadAttributes): string;
var
TheClassName, s: string;
IsClassName, IsProcType: boolean;
IsProcedure: Boolean;
IsFunction: Boolean;
IsOperator: Boolean;
EndPos: Integer;
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);
// build full class name
TheClassName:='';
if ([phpAddClassname,phpWithoutClassName]*Attr=[phpAddClassName]) then
TheClassName:=ExtractClassName(ProcNode,phpInUpperCase in Attr,true);
// reparse the clean source
InitExtraction;
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)
or (not WordIsCustomOperator.DoItCaseInsensitive(Src,CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)))
and (not AtomIsIdentifier) then exit;
if TheClassName<>'' then begin
s:=TheClassName+'.';
if phpInUpperCase in Attr then s:=UpperCaseStr(s);
if ExtractStreamEndIsIdentChar then
s:=' '+s;
ExtractMemStream.Write(s[1],length(s));
end;
if [phpWithoutClassName,phpWithoutName]*Attr=[] then begin
// read classname and name
repeat
ExtractNextAtom(true,Attr);
if Scanner.CompilerMode = cmDELPHI then
begin
// delphi generics
if AtomIsChar('<') then
begin
while not AtomIsChar('>') and (CurPos.EndPos < SrcLen) do
ExtractNextAtom(not (phpWithoutGenericParams in Attr),Attr);
ExtractNextAtom(not (phpWithoutGenericParams in Attr),Attr);
end;
end;
if CurPos.Flag<>cafPoint then break;
ExtractNextAtom(true,Attr);
if ((not IsOperator)
or (not WordIsCustomOperator.DoItCaseInsensitive(Src,CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)))
and (not AtomIsIdentifier) then exit;
until false;
end else begin
// read only part of name
repeat
ReadNextAtom;
if (Scanner.CompilerMode = cmDELPHI) and AtomIsChar('<') then
begin
while not AtomIsChar('>') and (CurPos.EndPos < SrcLen) do
ReadNextAtom;
ReadNextAtom;
end;
IsClassName:=(CurPos.Flag=cafPoint);
UndoReadNextAtom;
if IsClassName then begin
// read class name
ExtractNextAtom(not (phpWithoutClassName in Attr),Attr);
// read '.'
ExtractNextAtom(not (phpWithoutClassName in Attr),Attr);
if ((not IsOperator)
or (not WordIsCustomOperator.DoItCaseInsensitive(Src,CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)))
and (not AtomIsIdentifier) then exit;
end else begin
// read name
ExtractNextAtom(not (phpWithoutName in Attr),Attr);
break;
end;
until false;
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 then exit;
ExtractNextAtom(phpWithResultType in Attr,Attr);
if CurPos.Flag=cafPoint then begin
ExtractNextAtom(phpWithResultType in Attr,Attr);
if not AtomIsIdentifier 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
if ProcNode.FirstChild<>nil then
EndPos:=ProcNode.FirstChild.EndPos
else
EndPos:=SrcLen+1;
while (CurPos.StartPos<EndPos) do begin
if CurPos.Flag=cafSemicolon then begin
ExtractNextAtom(phpWithProcModifiers in Attr,Attr);
end else begin
if IsKeyWordCallingConvention.DoIdentifier(@Src[CurPos.StartPos])
then begin
ExtractNextAtom([phpWithCallingSpecs,phpWithProcModifiers]*Attr<>[],
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.ExtractProcedureHeader(CursorPos: TCodeXYPosition;
Attributes: TProcHeadAttributes; var ProcHead: string): boolean;
var
CleanCursorPos: integer;
ANode: TCodeTreeNode;
begin
Result:=false;
ProcHead:='';
BuildTreeAndGetCleanPos(trTillCursor,lsrEnd,CursorPos,CleanCursorPos,
[btSetIgnoreErrorPos,btCursorPosOutAllowed]);
ANode:=FindDeepestNodeAtPos(CleanCursorPos,True);
while (ANode<>nil) and (ANode.Desc<>ctnProcedure) do
ANode:=ANode.Parent;
if ANode=nil then exit;
ProcHead:=ExtractProcHead(ANode,Attributes);
Result:=true;
end;
function TPascalReaderTool.ExtractClassName(Node: TCodeTreeNode;
InUpperCase: boolean; WithParents: boolean): string;
var
ParamsNode: TCodeTreeNode;
ParamNode: TCodeTreeNode;
First: Boolean;
begin
Result:='';
while Node<>nil do begin
case Node.Desc of
ctnTypeDefinition,ctnGenericType:
begin
if Result<>'' then Result:='.'+Result;
if Node.Desc=ctnTypeDefinition then
Result:=GetIdentifier(@Src[Node.StartPos])+Result
else if Node.FirstChild<>nil then
begin
if (Scanner.CompilerMode = cmDELPHI) and (Node.Desc = ctnGenericType)
then begin
// extract generic type param names
ParamsNode:=Node.FirstChild.NextBrother;
First:=true;
while ParamsNode<>nil do begin
if ParamsNode.Desc=ctnGenericParams then begin
Result:='>'+Result;
ParamNode:=ParamsNode.FirstChild;
while ParamNode<>nil do begin
if ParamNode.Desc=ctnGenericParameter then begin
if First then
First:=false
else
Result:=','+Result;
Result:=GetIdentifier(@Src[ParamNode.StartPos])+Result;
end;
ParamNode:=ParamNode.NextBrother;
end;
Result:='<'+Result;
end;
ParamsNode:=ParamsNode.NextBrother;
end;
end;
Result:=GetIdentifier(@Src[Node.FirstChild.StartPos])+Result;
end;
if not WithParents then break;
end;
ctnParameterList:
break;
end;
Node:=Node.Parent;
end;
if InUpperCase then
Result:=UpperCaseStr(Result);
end;
function TPascalReaderTool.ExtractClassPath(Node: TCodeTreeNode): string;
var
InArray: Boolean;
begin
Result:='';
InArray:=false;
while Node<>nil do begin
case Node.Desc of
ctnTypeDefinition,ctnGenericType:
begin
if Result<>'' then Result:='.'+Result;
if Node.Desc=ctnTypeDefinition then
Result:=GetIdentifier(@Src[Node.StartPos])+Result
else if Node.FirstChild<>nil then
begin
if (Scanner.CompilerMode = cmDELPHI) and (Node.Desc = ctnGenericType) then
Result := Result + ExtractNode(Node.FirstChild.NextBrother, []);
Result:=GetIdentifier(@Src[Node.FirstChild.StartPos])+Result;
end;
end;
ctnParameterList:
break;
ctnRangedArrayType, ctnOpenArrayType:
begin
InArray := True;
Result := '[]' + Result;
end;
ctnVarDefinition:
if InArray then begin
Result := GetIdentifier(@Src[Node.StartPos]) + Result;
InArray := False;
end;
end;
Node:=Node.Parent;
end;
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 then exit;
MoveCursorToCleanPos(CurPos.StartPos);
ExtractProcHeadPos:=phepNone;
InitExtraction;
while (CurPos.StartPos<=SrcLen) do begin
ExtractNextAtom(true,Attr); // read ancestor/interface
if not AtomIsIdentifier 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;
AddParentClasses: boolean): string;
var
Part: 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);
repeat
ReadNextAtom;
if not AtomIsIdentifier then break;
Part:=GetAtom;
ReadNextAtom;
if (Scanner.CompilerMode = cmDELPHI) and AtomIsChar('<') then
begin { delphi generics }
Part := Part + GetAtom;
repeat
ReadNextAtom;
Part := Part + GetAtom;
until (CurPos.StartPos > SrcLen) or AtomIsChar('>');
ReadNextAtom;
end;
if (CurPos.Flag<>cafPoint) then break;
if Result<>'' then Result:=Result+'.';
Result:=Result+Part;
until false;
if not AddParentClasses then exit;
Part:=ExtractClassName(ProcNode,false,true);
if Part='' then exit;
Result:=Part+'.'+Result;
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;
InClass: Boolean;
begin
Result:=StartNode;
InClass:=FindClassOrInterfaceNode(StartNode)<>nil;
while (Result<>nil) do begin
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,'" Result=',CompareTextIgnoringSpace(CurProcHead,AProcHead,false)]);
if (CurProcHead<>'')
and (CompareTextIgnoringSpace(CurProcHead,AProcHead,false)=0) then
exit;
end;
end;
// next node
if InClass then
Result:=FindNextIdentNodeInClass(Result)
else
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.GetTopMostNodeOfType(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),
true,false,false,true);
if StartNode=nil then exit;
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),' ',dbgs(Attr),' ',StartNode.DescAsString);
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.LastChild;
while Result<>nil do begin
if Result.Desc in [ctnBeginBlock,ctnAsmBlock] then
exit;
Result:=Result.PriorBrother;
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;
// inherited is allowed
if UpAtomIs('INHERITED') then begin
ReadNextAtom;
if CurPos.Flag=cafSemicolon then begin
// semicolon is allowed
LastPos:=CurPos.EndPos;
ReadNextAtom;
if FindNextNonSpace(Src,LastPos)<>CurPos.StartPos then exit;
end;
end;
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
// this can be 'of object'
begin
//DebugLn(['TPascalReaderTool.MoveCursorToFirstProcSpecifier ',ProcNode.DescAsString,' ',ProcNode.StartPos]);
if (ProcNode<>nil) and (ProcNode.Desc in [ctnProcedureType,ctnProcedure]) then
ProcNode:=ProcNode.FirstChild;
if (ProcNode=nil) or (ProcNode.Desc<>ctnProcedureHead) then begin
RaiseException('Internal Error in'
+' TPascalParserTool.MoveCursorFirstProcSpecifier: '
+' (ProcNode=nil) or (ProcNode.Desc<>ctnProcedure)');
end;
if (ProcNode.LastChild<>nil) and (ProcNode.LastChild.Desc=ctnIdentifier) then
begin
// jump behind function result type
MoveCursorToCleanPos(ProcNode.LastChild.EndPos);
ReadNextAtom;
end else if (ProcNode.FirstChild<>nil)
and (ProcNode.FirstChild.Desc=ctnParameterList)
then begin
// jump behind parameter list
MoveCursorToCleanPos(ProcNode.FirstChild.EndPos);
ReadNextAtom;
end else begin
MoveCursorToNodeStart(ProcNode);
ReadNextAtom;
if AtomIsCustomOperator(true,false,false) then begin
// read name
ReadNextAtom;
while (CurPos.Flag=cafPoint) do begin
ReadNextAtom;
if CurPos.Flag in [cafPoint,cafRoundBracketOpen,cafEdgedBracketOpen,cafColon,cafEnd,cafSemicolon]
then break;
ReadNextAtom;
end;
end;
if (CurPos.Flag=cafRoundBracketOpen) then begin
// read paramlist
ReadTilBracketClose(false);
ReadNextAtom;
end;
end;
if (CurPos.Flag=cafColon) then begin
// read function result type
ReadNextAtom;
if AtomIsIdentifier then begin
ReadNextAtom;
while CurPos.Flag=cafPoint do begin
ReadNextAtom;
if not AtomIsIdentifier then break;
ReadNextAtom;
end;
end;
end;
// CurPos now stands on the first proc specifier or on a semicolon or on the syntax error
end;
function TPascalReaderTool.MoveCursorToProcSpecifier(ProcNode: TCodeTreeNode;
ProcSpec: TProcedureSpecifier): boolean;
begin
if ProcNode.FirstChild=nil then begin
exit(false);
end;
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 not SkipClassName then exit;
repeat
ReadNextAtom;
if CurPos.Flag<>cafPoint then begin
UndoReadNextAtom;
break;
end;
ReadNextAtom;
until not AtomIsIdentifier;
end;
procedure TPascalReaderTool.MoveCursorBehindProcName(ProcNode: TCodeTreeNode);
begin
if (ProcNode.FirstChild<>nil)
and (ProcNode.FirstChild.Desc=ctnProcedureHead) then
ProcNode:=ProcNode.FirstChild;
MoveCursorToNodeStart(ProcNode);
ReadNextAtom;
if AtomIsIdentifier then begin
ReadNextAtom;
while CurPos.Flag=cafPoint do begin
ReadNextAtom;
if not AtomIsIdentifier then exit;
ReadNextAtom;
end;
end else if CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen,cafColon]
then begin
end else begin
// operator
ReadNextAtom;
end;
end;
function TPascalReaderTool.PositionInProcName(ProcNode: TCodeTreeNode;
SkipClassName: boolean; CleanPos: integer): boolean;
begin
if (ProcNode.Desc=ctnProcedure) and (ProcNode.FirstChild<>nil)
and (ProcNode.FirstChild.Desc=ctnProcedureHead) then
ProcNode:=ProcNode.FirstChild;
if (CleanPos<ProcNode.StartPos) or (CleanPos>ProcNode.EndPos) then exit(false);
MoveCursorToNodeStart(ProcNode);
ReadNextAtom;
if (ProcNode.Desc=ctnProcedure) then begin
if UpAtomIs('CLASS') then ReadNextAtom;
ReadNextAtom; // skip proc keyword
end;
if CleanPos<CurPos.StartPos then exit(false);
while CurPos.Flag=cafWord do begin
ReadNextAtom;
if CurPos.Flag<>cafPoint then begin
UndoReadNextAtom;
break;
end;
ReadNextAtom;
end;
// CurPos is now on the proc name
if CleanPos>CurPos.EndPos then exit(false);
if SkipClassName and (CleanPos<CurPos.StartPos) then exit(false);
Result:=true;
end;
function TPascalReaderTool.PositionInFuncResultName(ProcNode: TCodeTreeNode;
CleanPos: integer): boolean;
// true if position between ) and :
var
Node: TCodeTreeNode;
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;
if ProcNode.Desc=ctnProcedureHead then begin
Node:=ProcNode.FirstChild;
while (Node<>nil) and (Node.Desc<>ctnIdentifier) do begin
if (Node.Desc=ctnIdentifier)
and (CleanPos>=Node.StartPos) and (CleanPos<=Node.EndPos) then
exit(true);
Node:=Node.NextBrother;
end;
end;
// read behind parameter list
if ProcNode.Desc<>ctnProcedureHead then exit;
if (ProcNode.FirstChild<>nil) and (ProcNode.FirstChild.Desc=ctnParameterList)
then begin
if (CleanPos<ProcNode.FirstChild.EndPos) then
exit;
MoveCursorToCleanPos(ProcNode.FirstChild.EndPos);
end else begin
MoveCursorToNodeStart(ProcNode);
ReadNextAtom;
while AtomIsIdentifier do begin
ReadNextAtom;
if (CurPos.Flag<>cafPoint) then break;
ReadNextAtom;
end;
if CurPos.Flag=cafRoundBracketOpen then
if not ReadTilBracketClose(false) then exit;
end;
if CurPos.StartPos>CleanPos then exit;
// read optional result variable (e.g. operator can have them)
ReadNextAtom;
if AtomIsIdentifier 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 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;
procedure TPascalReaderTool.MoveCursorBehindPropName(PropNode: TCodeTreeNode);
begin
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 then exit;
ReadNextAtom;
end;
function TPascalReaderTool.ProcNodeHasSpecifier(ProcNode: TCodeTreeNode;
ProcSpec: TProcedureSpecifier): boolean;
begin
Result:=false;
if ProcNode=nil then exit;
if ProcNode.Desc=ctnProcedureHead then
ProcNode:=ProcNode.Parent;
{$IFDEF CheckNodeTool}
if ProcNode.Desc<>ctnProcedure then begin
DebugLn(['TPascalReaderTool.ProcNodeHasSpecifier Desc=',ProcNode.DescAsString]);
CTDumpStack;
RaiseException('[TPascalReaderTool.ProcNodeHasSpecifier] '
+'internal error: invalid ProcNode');
end;
{$ENDIF}
if (ProcNode.FirstChild=nil)
or ((ProcNode.SubDesc and ctnsNeedJITParsing)>0) then
BuildSubTreeForProcHead(ProcNode);
// 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);
repeat
ReadNextAtom;
if not AtomIsIdentifier then exit(nil);
Result:=@Src[CurPos.StartPos];
ReadNextAtom;
until CurPos.Flag<>cafPoint;
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.ExtractIdentifierWithPoints(StartPos: integer;
ExceptionOnError: boolean): string;
begin
Result:='';
MoveCursorToCleanPos(StartPos);
ReadNextAtom;
if not AtomIsIdentifierE(ExceptionOnError) then exit;
Result:=GetAtom;
repeat
ReadNextAtom;
if CurPos.Flag<>cafPoint then
exit;
ReadNextAtom;
if not AtomIsIdentifierE(ExceptionOnError) then exit;
Result+='.'+GetAtom;
until false;
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 then exit;
ExtractNextAtom(phpWithResultType in Attr,Attr);
if CurPos.Flag=cafPoint then begin
// unit.type
ExtractNextAtom(phpWithResultType in Attr,Attr);
if not AtomIsIdentifier 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 APos<SrcLen do begin
if (Src[APos]='#') then begin
// skip char constant
inc(APos);
if IsNumberChar[Src[APos]] then begin
while (APos<CurPos.EndPos) and IsNumberChar[Src[APos]] do
inc(APos)
end else if Src[APos]='$' then begin
while (APos<CurPos.EndPos) and IsHexNumberChar[Src[APos]] do
inc(APos);
end;
end else if (Src[APos]='''') then begin
inc(APos);
repeat
// read identifier chars
IdentStartPos:=APos;
while (APos<SrcLen) and (IsIdentChar[Src[APos]]) do
inc(APos);
IdentEndPos:=APos;
if IdentStartPos<MinPos then IdentStartPos:=MinPos;
if IdentEndPos>MaxPos 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<SrcLen) and (Src[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
Run: Integer;
NumberStart: PChar;
ResultLen: Integer;
Number: Integer;
p: PChar;
begin
Result:='';
if StartPos>SrcLen then exit;
// first read and calculate the resulting length, then copy the chars
for Run:=1 to 2 do begin
ResultLen:=0;
p:=@Src[StartPos];
while true do begin
case p^ of
'''':
begin
// read string
inc(p);
while true do begin
if p^='''' then begin
if p[1]='''' then begin
// a double ' means a single '
inc(ResultLen);
if Run=2 then Result[ResultLen]:='''';
inc(p,2);
end else begin
// a single ' means end of string constant
inc(p);
break;
end;
end else begin
// normal char
inc(ResultLen);
if Run=2 then Result[ResultLen]:=p^;
inc(p);
end;
end;
end;
'#':
begin
// read char constant
inc(p);
NumberStart:=p;
if IsNumberChar[p^] then begin
// read decimal number
while IsNumberChar[p^] do
inc(p);
Number:=StrToIntDef(copy(Src,NumberStart-PChar(Src)+1,p-NumberStart),-1);
end else if p^='$' then begin
// read hexnumber
inc(p);
while IsHexNumberChar[p^] do
inc(p);
Number:=HexStrToIntDef(NumberStart,-1);
end else
Number:=-1;
// add special character
if (Number<0) or (Number>255) then break;
inc(ResultLen);
if Run=2 then Result[ResultLen]:=chr(Number);
end;
'^':
begin
inc(p);
if p^ in ['A'..'Z'] then begin
inc(ResultLen);
if Run=2 then Result[ResultLen]:=chr(ord(p^)-ord('A'));
end else begin
break;
end;
end;
else
break;
end;
end;
if Run=1 then SetLength(Result,ResultLen);
end;
end;
function TPascalReaderTool.GetNodeIdentifier(Node: TCodeTreeNode): PChar;
begin
Result:=nil;
if (Node=nil) or (Node.StartPos>SrcLen) 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.GetHintModifiers(Node: TCodeTreeNode): TPascalHintModifiers;
function IsHintModifier: boolean;
begin
if CurPos.Flag<>cafWord then exit(false);
Result:=true;
if UpAtomIs('PLATFORM') then
Include(GetHintModifiers,phmPlatform)
else if UpAtomIs('UNIMPLEMENTED') then
Include(GetHintModifiers,phmUnimplemented)
else if UpAtomIs('LIBRARY') then
Include(GetHintModifiers,phmLibrary)
else if UpAtomIs('EXPERIMENTAL') then
Include(GetHintModifiers,phmExperimental)
else if UpAtomIs('DEPRECATED') then
Include(GetHintModifiers,phmDeprecated)
else
Result:=false;
end;
begin
Result:=[];
if Node=nil then exit;
case Node.Desc of
ctnProgram,ctnPackage,ctnLibrary,ctnUnit:
begin
MoveCursorToNodeStart(Node);
ReadNextAtom;
if not (UpAtomIs('PROGRAM') or UpAtomIs('PACKAGE') or UpAtomIs('LIBRARY')
or UpAtomIs('UNIT')) then exit;
ReadNextAtom;// name
while IsHintModifier do ReadNextAtom;
end;
ctnProcedure,ctnProcedureType,ctnProcedureHead:
begin
if Node.Desc<>ctnProcedureHead then begin
Node:=Node.FirstChild;
if Node=nil then exit;
end;
MoveCursorToFirstProcSpecifier(Node);
// ToDo:
end;
ctnProperty:
begin
Node:=Node.LastChild;
while Node<>nil do begin
if Node.Desc=ctnHintModifier then begin
MoveCursorToNodeStart(Node);
ReadNextAtom;
IsHintModifier;
end;
Node:=Node.PriorBrother;
end;
end;
ctnVarDefinition,ctnConstant,ctnConstDefinition,
ctnTypeDefinition,ctnGenericType:
begin
Node:=FindTypeNodeOfDefinition(Node);
if Node=nil then exit;
while (Node<>nil) do begin
if Node.Desc=ctnHintModifier then begin
MoveCursorToNodeStart(Node);
ReadNextAtom;
IsHintModifier;
end;
Node:=Node.NextBrother;
end;
end;
end;
end;
procedure TPascalReaderTool.ForEachIdentifierInCleanSrc(StartPos,
EndPos: integer; SkipComments: boolean; Node: TCodeTreeNode;
const OnIdentifier: TOnEachPRIdentifier; Data: pointer; var Abort: boolean);
var
CommentLvl: Integer;
InStrConst: Boolean;
p: PChar;
EndP: Pointer;
Range: TEPRIRange;
procedure SkipIdentifier; inline;
begin
while (p<EndP) and IsIdentChar[p^] do inc(p);
end;
begin
//debugln(['TPascalReaderTool.ForEachIdentifierInCleanSrc Node=',Node.DescAsString,' "',dbgstr(Src,StartPos,EndPos-StartPos),'"']);
if (StartPos<1) then
StartPos:=1;
if StartPos>SrcLen then exit;
if EndPos>SrcLen then EndPos:=SrcLen+1;
if StartPos>=EndPos then exit;
Range:=epriInCode;
p:=@Src[StartPos];
EndP:=p+EndPos-StartPos;
while p<EndP do begin
case p^ of
'{':
begin
inc(p);
if p^=#3 then begin
// codetools skip comment {#3 #3}
inc(p);
repeat
if p>=EndP then exit;
if (p^=#3) and (p[1]='}')
then begin
inc(p,2);
break;
end;
inc(p);
until false;
end else begin
// pascal comment {}
CommentLvl:=1;
InStrConst:=false;
if p^='$' then
Range:=epriInDirective
else
Range:=epriInComment;
repeat
if p>=EndP then exit;
case p^ of
'{': if Scanner.NestedComments then inc(CommentLvl);
'}':
begin
dec(CommentLvl);
if CommentLvl=0 then break;
end;
'a'..'z','A'..'Z','_':
if not InStrConst then begin
if not SkipComments then begin
OnIdentifier(Self,p-PChar(Src)+1,Range,Node,Data,Abort);
SkipIdentifier;
if Abort then exit;
end;
while (p<EndP) and IsIdentChar[p^] do inc(p);
end;
'''':
InStrConst:=not InStrConst;
#10,#13:
InStrConst:=false;
end;
inc(p);
until false;
inc(p);
//debugln(StartPos,' ',copy(Src,CommentStart,StartPos-CommentStart));
end;
end;
'/': // Delphi comment
if p[1]<>'/' then begin
inc(p);
end else begin
inc(p,2);
InStrConst:=false;
repeat
if p>=EndP then exit;
case p^ of
#10,#13:
break;
'a'..'z','A'..'Z','_':
if not InStrConst then begin
if not SkipComments then begin
OnIdentifier(Self,p-PChar(Src)+1,Range,Node,Data,Abort);
SkipIdentifier;
if Abort then exit;
end;
while (p<EndP) and IsIdentChar[p^] do inc(p);
end;
'''':
InStrConst:=not InStrConst;
end;
inc(p);
until false;
inc(p);
if (p<EndP) and (p^ in [#10,#13])
and (p[-1]<>p^) then
inc(p);
end;
'(': // turbo pascal comment
if (p[1]<>'*') then begin
inc(p);
end else begin
inc(p,3);
InStrConst:=false;
repeat
if p>=EndP then exit;
case p^ of
')':
if p[-1]='*' then break;
'a'..'z','A'..'Z','_':
if not InStrConst then begin
if not SkipComments then begin
OnIdentifier(Self,p-PChar(Src)+1,Range,Node,Data,Abort);
SkipIdentifier;
if Abort then exit;
end;
SkipIdentifier;
end;
'''':
InStrConst:=not InStrConst;
#10,#13:
InStrConst:=false;
end;
inc(p);
until false;
inc(p);
end;
'a'..'z','A'..'Z','_':
begin
OnIdentifier(Self,p-PChar(Src)+1,epriInCode,Node,Data,Abort);
SkipIdentifier;
if Abort then exit;
end;
'''':
begin
// skip string constant
inc(p);
while p<EndP do begin
if (not (p^ in ['''',#10,#13])) then
inc(p)
else begin
inc(p);
break;
end;
end;
end;
else
inc(p);
end;
end;
end;
procedure TPascalReaderTool.ForEachIdentifierInNode(Node: TCodeTreeNode;
SkipComments: boolean; const OnIdentifier: TOnEachPRIdentifier;
Data: Pointer; var Abort: boolean);
var
StartPos: Integer;
EndPos: Integer;
Child: TCodeTreeNode;
begin
//debugln(['TPascalReaderTool.ForEachIdentifierInNode START ',Node.DescAsString]);
if NodeNeedsBuildSubTree(Node) then
BuildSubTree(Node);
if Node.FirstChild<>nil then begin
EndPos:=Node.StartPos;
Child:=Node.FirstChild;
while Child<>nil do begin
// scan in front of child
ForEachIdentifierInCleanSrc(EndPos,Child.StartPos,SkipComments,
Node,OnIdentifier,Data,Abort);
if Abort then exit;
// scan child
ForEachIdentifierInNode(Child,SkipComments,OnIdentifier,Data,Abort);
if Abort then exit;
EndPos:=Child.EndPos;
Child:=Child.NextBrother;
end;
// scan behind children
ForEachIdentifierInCleanSrc(EndPos,Node.EndPos,SkipComments,
Node,OnIdentifier,Data,Abort);
end else begin
// leaf node
StartPos:=Node.StartPos;
EndPos:=Node.EndPos;
// nodes without children can overlap with their NextBrother
if (Node.NextBrother<>nil)
and (Node.NextBrother.StartPos<EndPos) then
EndPos:=Node.NextBrother.StartPos;
// scan node range
ForEachIdentifierInCleanSrc(StartPos,EndPos,SkipComments,
Node,OnIdentifier,Data,Abort);
end;
end;
procedure TPascalReaderTool.ForEachIdentifier(SkipComments: boolean;
const OnIdentifier: TOnEachPRIdentifier; Data: Pointer);
var
Node: TCodeTreeNode;
Abort: boolean;
begin
//debugln(['TPascalReaderTool.ForEachIdentifier START']);
Node:=Tree.Root;
Abort:=false;
while Node<>nil do begin
ForEachIdentifierInNode(Node,SkipComments,OnIdentifier,Data,Abort);
if Abort then exit;
Node:=Node.NextBrother;
end;
end;
function TPascalReaderTool.FindVarNode(StartNode: TCodeTreeNode;
const UpperVarName: string): TCodeTreeNode;
var
InClass: Boolean;
begin
Result:=StartNode;
InClass:=FindClassOrInterfaceNode(StartNode)<>nil;
while Result<>nil do begin
if (Result.Desc=ctnVarDefinition)
and (CompareNodeIdentChars(Result,UpperVarName)=0) then
exit;
if InClass then
Result:=FindNextIdentNodeInClass(Result)
else
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<>ctnVarDefinition then exit(nil);
Result:=Result.NextBrother;
end;
end;
function TPascalReaderTool.FindClassNode(StartNode: TCodeTreeNode;
const AClassName: string; IgnoreForwards, IgnoreNonForwards: boolean): TCodeTreeNode;
// search for class like types on same level
var
ANode, CurClassNode: TCodeTreeNode;
NameNode: TCodeTreeNode;
p: PChar;
begin
ANode:=StartNode;
Result:=nil;
if AClassName='' then exit;
p:=PChar(AClassName);
while (ANode<>nil) do begin
if ANode.Desc in [ctnTypeDefinition,ctnGenericType] then begin
//debugln(['TPascalReaderTool.FindClassNode ',GetIdentifier(@Src[ANode.StartPos])]);
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) and (ANode.FirstChild<>nil) then
NameNode:=ANode.FirstChild;
//debugln(['TPascalReaderTool.FindClassNode class name = "',GetIdentifier(@Src[NameNode.StartPos]),'"']);
if NameNode.StartPos>SrcLen then exit;
if CompareIdentifiers(p,@Src[NameNode.StartPos])=0 then begin
Result:=FindNestedClass(CurClassNode,p,true);
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;
p: PChar;
begin
ANode:=StartNode;
p:=PChar(AClassName);
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(p,@Src[ANode.StartPos])=0 then begin
Result:=FindNestedClass(CurClassNode,p,true);
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.FindNestedClass(RootClassNode: TCodeTreeNode;
AClassName: PChar; SkipFirst: boolean): TCodeTreeNode;
var
p: PChar;
Node: TCodeTreeNode;
EndNode: TCodeTreeNode;
begin
Result:=nil;
if RootClassNode=nil then exit;
if AClassName=nil then exit;
p:=AClassName;
if SkipFirst then begin
while IsIdentChar[p^] do inc(p);
if p^='<' then
begin
while not (p^ in [#0,'>']) do Inc(p);
if p^ = '>' then Inc(p);
end;
if p^=#0 then exit(RootClassNode);
if p^<>'.' then exit;
inc(p);
end;
//debugln(['TPascalReaderTool.FindNestedClass p="',p,'"']);
if not IsIdentStartChar[p^] then exit;
EndNode:=RootClassNode.NextSkipChilds;
Node:=RootClassNode.Next;
while Node<>EndNode do begin
// debugln(['TPascalReaderTool.FindNestedClass Node=',node.DescAsString]);
if Node.Desc in [ctnTypeDefinition,ctnGenericType] then begin
if (Node.LastChild<>nil) and (Node.LastChild.Desc in AllClasses) then begin
if ((Node.Desc=ctnTypeDefinition)
and (CompareIdentifierPtrs(p,@Src[Node.StartPos])=0))
or ((Node.FirstChild.Desc=ctnGenericName)
and (CompareIdentifierPtrs(p,@Src[Node.FirstChild.StartPos])=0))
then begin
// class found
Node:=Node.LastChild;
while IsIdentChar[p^] do inc(p);
if p^=#0 then exit(Node);
if p^<>'.' then exit;
Result:=FindNestedClass(Node,p+1,false);
exit;
end;
end;
end;
if Node.Desc in AllClassSections then
Node:=Node.Next
else
Node:=Node.NextSkipChilds;
end;
end;
function TPascalReaderTool.FindClassNode(CursorNode: TCodeTreeNode): TCodeTreeNode;
// find class node of a node in a procedure (declaration or body)
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,true);
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.GetClassVisibility(Node: TCodeTreeNode
): TCodeTreeNodeDesc;
begin
Result:=ctnNone;
if Node=nil then exit;
if Node.Desc=ctnProcedureHead then
Node:=Node.Parent;
if not (Node.Desc in AllClassSections) then begin
Node:=Node.Parent;
if Node=nil then exit;
end;
if Node.Desc in AllClassSubSections then
Node:=Node.Parent;
if Node.Desc in AllClassBaseSections then
Result:=Node.Desc;
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
Result:=Result.NextBrother;
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
if (ClassNode=nil) then exit(nil);
Result:=FindNextIdentNodeInClass(ClassNode.FirstChild);
end;
function TPascalReaderTool.FindLastIdentNodeInClass(ClassNode: TCodeTreeNode
): TCodeTreeNode;
begin
if (ClassNode=nil) then exit(nil);
Result:=ClassNode.LastChild;
if Result=nil then exit;
while (Result.FirstChild<>nil) and (Result.Desc in AllClassSections) do
Result:=Result.LastChild;
if not (Result.Desc in AllClassSections) then
Result:=FindPriorIdentNodeInClass(Result);
end;
function TPascalReaderTool.FindNextIdentNodeInClass(Node: TCodeTreeNode
): TCodeTreeNode;
// Node must be nil or a class section or an identifier node in a class
begin
Result:=Node;
if Result=nil then exit;
repeat
// descend into class sections, skip empty class sections
if (Result.FirstChild<>nil) and (Result.Desc in AllClassSections) then
Result:=Result.FirstChild
else begin
while Result.NextBrother=nil do begin
Result:=Result.Parent;
if (Result=nil) or (not (Result.Desc in AllClassSections)) then
exit(nil);
end;
Result:=Result.NextBrother
end;
until not (Result.Desc in AllClassSections);
end;
function TPascalReaderTool.FindPriorIdentNodeInClass(Node: TCodeTreeNode
): TCodeTreeNode;
begin
Result:=Node;
if Result=nil then exit;
repeat
if Result.PriorBrother<>nil then begin
Result:=Result.PriorBrother;
while (Result.LastChild<>nil) and (Result.Desc in AllClassSections) do
Result:=Result.LastChild;
end else if Result.Parent.Desc in AllClassSections then
Result:=Result.Parent
else
exit(nil);
until not (Result.Desc in AllClassSections);
end;
function TPascalReaderTool.ClassSectionNodeStartsWithWord(ANode: TCodeTreeNode
): boolean;
begin
Result:=(ANode<>nil) and (ANode.StartPos<ANode.EndPos)
and (IsIdentStartChar[Src[ANode.StartPos]]);
end;
function TPascalReaderTool.IsClassNode(Node: TCodeTreeNode): boolean;
begin
Result:=(Node<>nil) and (Node.Desc=ctnClass);
end;
function TPascalReaderTool.FindInheritanceNode(ClassNode: TCodeTreeNode): TCodeTreeNode;
begin
Result:=ClassNode.FirstChild;
while (Result<>nil) and (Result.Desc in [ctnClassSealed,ctnClassAbstract,ctnClassExternal]) do
Result:=Result.NextBrother;
if (Result<>nil) and (Result.Desc<>ctnClassInheritance) then
Result:=nil;
end;
function TPascalReaderTool.ExtractRecordCaseType(RecordCaseNode: TCodeTreeNode): string;
// case a:b.c of
// case a:(b,c) of
var
VarNode: TCodeTreeNode;
begin
Result:='';
VarNode:=RecordCaseNode.FirstChild;
if VarNode=nil then exit;
if VarNode.FirstChild<>nil then
Result:=ExtractNode(RecordCaseNode.FirstChild,[]);
end;
function TPascalReaderTool.GetSourceType: TCodeTreeNodeDesc;
begin
if Tree.Root<>nil then
Result:=Tree.Root.Desc
else
Result:=ctnNone;
end;
function TPascalReaderTool.PositionInSourceName(CleanPos: integer): boolean;
var
NamePos: TAtomPosition;
begin
Result:=false;
if not GetSourceNamePos(NamePos) then exit;
Result:=(CleanPos>=NamePos.StartPos) and (CleanPos<NamePos.EndPos);
end;
function TPascalReaderTool.ExtractSourceName: string;
begin
Result:='';
if Tree.Root<>nil then begin
MoveCursorToNodeStart(Tree.Root);
ReadNextAtom; // read source type 'program', 'unit' ...
if (Tree.Root.Desc<>ctnProgram) or UpAtomIs('PROGRAM') then begin
ReadNextAtom; // read name
if AtomIsIdentifier then begin
Result:=copy(Src,CurPos.StartPos,CurPos.EndPos-CurPos.StartPos);
ReadNextAtom;
while CurPos.Flag=cafPoint do begin
ReadNextAtom;
if not AtomIsIdentifier then exit;
Result:=Result+'.'+copy(Src,CurPos.StartPos,CurPos.EndPos-CurPos.StartPos);
ReadNextAtom;
end;
exit;
end;
end;
end;
if (Tree.Root<>nil) and (Tree.Root.Desc=ctnProgram) then
// a program without the 'program' header uses the file name as name
Result:=ExtractFileNameOnly(MainFilename)
else
Result:='';
end;
function TPascalReaderTool.GetSourceNamePos(out NamePos: TAtomPosition
): boolean;
begin
Result:=false;
NamePos.StartPos:=-1;
if Tree.Root=nil then exit;
MoveCursorToNodeStart(Tree.Root);
ReadNextAtom; // read source type 'program', 'unit' ...
if (Tree.Root.Desc=ctnProgram) and (not UpAtomIs('PROGRAM')) then exit;
ReadNextAtom; // read name
if not AtomIsIdentifier then exit;
NamePos:=CurPos;
Result:=true;
ReadNextAtom;
while CurPos.Flag=cafPoint do begin
ReadNextAtom;
if not AtomIsIdentifier then exit;
NamePos.EndPos:=CurPos.EndPos;
ReadNextAtom;
end;
end;
function TPascalReaderTool.GetSourceName(DoBuildTree: boolean): string;
begin
Result:='';
if DoBuildTree then
BuildTree(lsrSourceName);
CachedSourceName:=ExtractSourceName;
Result:=CachedSourceName;
end;
function TPascalReaderTool.NodeIsInAMethod(Node: TCodeTreeNode): boolean;
begin
Result:=false;
while (Node<>nil) 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 then exit;
ReadNextAtom;
if (CurPos.Flag<>cafPoint) then exit;
Result:=true;
exit;
end;
end;
function TPascalReaderTool.GetMethodOfBody(Node: TCodeTreeNode): TCodeTreeNode;
begin
Result:=Node;
while (Result<>nil) and not NodeIsMethodBody(Result) do
Result:=Result.Parent;
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;
if UpAtomIs('CLASS') then ReadNextAtom;
Result:=UpAtomIs('CONSTRUCTOR');
if not Result and UpAtomIs('FUNCTION')
and ([cmsObjectiveC1,cmsObjectiveC2]*Scanner.CompilerModeSwitches<>[]) then
begin
ProcNode:=ProcNode.FirstChild;
if ProcNode=nil then exit;
if (ProcNode.SubDesc and ctnsNeedJITParsing)>0 then
BuildSubTreeForProcHead(ProcNode);
ProcNode:=ProcNode.FirstChild;
if (ProcNode=nil) then exit;
if ProcNode.Desc=ctnParameterList then
ProcNode:=ProcNode.NextBrother;
if (ProcNode=nil) then exit;
MoveCursorToNodeStart(ProcNode);
ReadNextAtom;
Result:=UpAtomIs('ID');
end;
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;
MoveCursorToNodeStart(ProcNode);
ReadNextAtom;
if UpAtomIs('CLASS') then ReadNextAtom;
Result:=UpAtomIs('OPERATOR');
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 (CleanPos<StartPos+GetIdentLen(@Src[StartPos]));
end;
function TPascalReaderTool.MoveCursorToParameterSpecifier(
DefinitionNode: TCodeTreeNode): boolean;
begin
Result:=false;
if (DefinitionNode=nil) or (DefinitionNode.Desc<>ctnVarDefinition)
or (DefinitionNode.Parent=nil)
or (DefinitionNode.Parent.Desc<>ctnParameterList) then exit;
// find first variable node of this type (e.g. var a,b,c,d: integer)
DefinitionNode:=GetFirstGroupVarNode(DefinitionNode);
if DefinitionNode.PriorBrother<>nil then
MoveCursorToCleanPos(DefinitionNode.PriorBrother.EndPos)
else
MoveCursorToCleanPos(DefinitionNode.Parent.StartPos);
ReadNextAtom;
while (CurPos.StartPos<DefinitionNode.StartPos) do ReadNextAtom;
UndoReadNextAtom;
Result:=CurPos.Flag=cafWord;
end;
function TPascalReaderTool.GetFirstGroupVarNode(VarNode: TCodeTreeNode): TCodeTreeNode;
begin
Result:=VarNode;
if (VarNode=nil) or (VarNode.Desc<>ctnVarDefinition) then exit;
while VarNode<>nil do begin
VarNode:=VarNode.PriorBrother;
if (VarNode=nil) or (VarNode.Desc<>ctnVarDefinition)
or (VarNode.FirstChild<>nil) then exit;
Result:=VarNode;
end;
end;
function TPascalReaderTool.FindEndOfWithVar(WithVarNode: TCodeTreeNode): integer;
begin
MoveCursorToCleanPos(WithVarNode.StartPos);
if not ReadTilVariableEnd(true,true) then exit(-1);
UndoReadNextAtom;
Result:=CurPos.EndPos;
end;
function TPascalReaderTool.NodeIsIdentifierInInterface(Node: TCodeTreeNode): boolean;
// true if identifier is visible from other units (without prefixing)
begin
case Node.Desc of
ctnEnumIdentifier:
Result:=true;
ctnVarDefinition:
Result:=(Node.Parent.Desc=ctnVarSection)
and (Node.Parent.Parent.Desc=ctnInterface);
ctnConstDefinition:
Result:=(Node.Parent.Desc=ctnConstSection)
and (Node.Parent.Parent.Desc=ctnInterface);
ctnTypeDefinition,ctnGenericType:
Result:=(Node.Parent.Desc=ctnTypeSection)
and (Node.Parent.Parent.Desc=ctnInterface);
ctnProcedure,ctnProperty:
Result:=Node.Parent.Desc=ctnInterface;
ctnProcedureHead:
Result:=(Node.Parent.Desc=ctnProcedure)
and (Node.Parent.Parent.Desc=ctnInterface);
end;
Result:=false;
end;
function TPascalReaderTool.NodeCanHaveForwardType(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.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.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 PropNode.Desc<>ctnProperty then exit;
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;
UpperKeyword: string; ExceptionOnNotFound: boolean): boolean;
// true if cursor is on keyword
begin
// ToDo: ppu, dcu
Result:=false;
if not MoveCursorToPropName(PropNode) then exit;
if not AtomIsIdentifierE(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 AtomIsIdentifierE(ExceptionOnNotFound) then exit;
ReadNextAtom;
if CurPos.Flag=cafPoint then begin
ReadNextAtom;
if not AtomIsIdentifierE(ExceptionOnNotFound) then exit;
ReadNextAtom;
end;
end;
UpperKeyword:=UpperCaseStr(UpperKeyword);
// read specifiers
while not (CurPos.Flag in [cafSemicolon,cafNone]) do begin
if WordIsPropertySpecifier.DoIdentifier(@Src[CurPos.StartPos])
then begin
if UpAtomIs(UpperKeyword) 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(UpperKeyword))=0 then exit(true);
end else if UpAtomIs('ENUMERATOR') then begin
if CompareIdentifierPtrs(@Src[CurPos.StartPos],Pointer(UpperKeyword))=0 then exit(true);
ReadNextAtom;
if not AtomIsIdentifier then exit;
end else
exit;
ReadNextAtom;
end;
end;
function TPascalReaderTool.ProcNodeHasParamList(ProcNode: TCodeTreeNode): boolean;
begin
// ToDo: ppu, dcu
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<>ctnProcedureHead then exit;
if ProcNode.FirstChild<>nil then begin
Result:=ProcNode.FirstChild.Desc=ctnParameterList;
exit;
end;
MoveCursorBehindProcName(ProcNode);
Result:=CurPos.Flag=cafRoundBracketOpen;
end;
function TPascalReaderTool.ProcNodeHasOfObject(ProcNode: TCodeTreeNode
): boolean;
begin
// ToDo: ppu, dcu
Result:=false;
if (ProcNode=nil) or (ProcNode.Desc<>ctnProcedureType) then exit;
MoveCursorToFirstProcSpecifier(ProcNode);
Result:=UpAtomIs('OF') and ReadNextUpAtomIs('OBJECT');
end;
function TPascalReaderTool.GetProcParamList(ProcNode: TCodeTreeNode;
Parse: boolean): TCodeTreeNode;
begin
Result:=ProcNode;
if Result=nil then exit;
if Result.Desc=ctnProcedure then begin
Result:=Result.FirstChild;
if Result=nil then exit;
end;
if Result.Desc<>ctnProcedureHead then exit(nil);
if Parse then
BuildSubTreeForProcHead(Result);
Result:=Result.FirstChild;
if Result=nil then exit;
if Result.Desc<>ctnParameterList then exit(nil);
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;
function TPascalReaderTool.ReadNextUsedUnit(out UnitNameRange,
InAtom: TAtomPosition; SyntaxExceptions: boolean): boolean;
// after reading CurPos is on atom behind, i.e. comma or semicolon
begin
Result:=false;
if not AtomIsIdentifierE(SyntaxExceptions) then exit;
UnitNameRange:=CurPos;
repeat
ReadNextAtom;
if CurPos.Flag<>cafPoint then break;
ReadNextAtom;
if not AtomIsIdentifierE(SyntaxExceptions) then exit;
UnitNameRange.EndPos:=CurPos.EndPos;
until false;
if UpAtomIs('IN') then begin
ReadNextAtom; // read filename
if not AtomIsStringConstant then begin
if not SyntaxExceptions then exit;
RaiseStrConstExpected;
end;
InAtom:=CurPos;
ReadNextAtom; // read comma or semicolon
end else begin
InAtom:=CleanAtomPosition;
end;
Result:=true;
end;
procedure TPascalReaderTool.ReadPriorUsedUnit(out UnitNameRange,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;
AtomIsIdentifierE;
UnitNameRange:=CurPos;
repeat
ReadPriorAtom;
if CurPos.Flag<>cafPoint then break;
ReadPriorAtom;
AtomIsIdentifierE;
UnitNameRange.StartPos:=CurPos.StartPos;
until false;
end;
function TPascalReaderTool.ExtractUsedUnitNameAtCursor(InFilename: PAnsiString): string;
begin
Result:='';
if InFilename<>nil then
InFilename^:='';
while CurPos.Flag=cafWord do begin
if Result<>'' then
Result:=Result+'.';
Result:=Result+GetAtom;
ReadNextAtom;
if CurPos.Flag<>cafPoint then break;
ReadNextAtom;
end;
if UpAtomIs('IN') then begin
ReadNextAtom;
if not AtomIsStringConstant then exit;
if InFilename<>nil then
InFilename^:=copy(Src,CurPos.StartPos+1,CurPos.EndPos-CurPos.StartPos-2);
ReadNextAtom;
end;
end;
function TPascalReaderTool.ExtractUsedUnitName(UseUnitNode: TCodeTreeNode;
InFilename: PAnsiString): string;
// after reading CurPos is on atom behind, i.e. comma or semicolon
begin
Result:='';
if InFilename<>nil then InFilename^:='';
if (UseUnitNode=nil) or (UseUnitNode.Desc<>ctnUseUnit) then exit;
MoveCursorToCleanPos(UseUnitNode.StartPos);
ReadNextAtom;
Result:=ExtractUsedUnitNameAtCursor(InFilename);
end;
function TPascalReaderTool.ReadAndCompareUsedUnit(const AnUnitName: string): boolean;
// after reading cursor is on atom behind unit name
var
p: PChar;
begin
Result:=false;
if IsDottedIdentifier(AnUnitName) then
p:=PChar(AnUnitName)
else
p:=nil;
repeat
if not AtomIsIdentifier then exit;
if (p<>nil) then begin
if CompareIdentifiers(p,@Src[CurPos.StartPos])=0 then
inc(p,CurPos.EndPos-CurPos.StartPos)
else
p:=nil;
end;
ReadNextAtom;
if CurPos.Flag<>cafPoint then begin
// end of unit name
Result:=(p<>nil) and (p^=#0);
exit;
end;
// dot
if (p<>nil) then begin
if p='.' then
inc(p)
else
p:=nil;
end;
ReadNextAtom;
until false;
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(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(StartPos: integer;
const CommentText: string;
SearchInParentNode, WithCommentBounds, CaseSensitive,
IgnoreSpaces, CompareOnlyStart: boolean;
out CommentStart, CommentEnd: integer): boolean;
// searches a comment in front of StartPos starting with CommentText.
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);
'{':
if (CompareStartPos<SrcLen) and (Src[CompareStartPos+1]=#3) then
// the codetools skip comment is no real comment
exit
else
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 CompareStartPos>CompareEndPos then exit;
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;
CommentStartPos: LongInt;
begin
Result:=false;
if StartPos>SrcLen then
StartPos:=SrcLen+1;
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;
p:=CurPos.EndPos;
//debugln('TPascalReaderTool.FindCommentInFront B Area="',copy(Src,CurPos.StartPos,StartPos-CurPos.StartPos),'"');
FoundStartPos:=-1;
repeat
//debugln('TPascalReaderTool.FindCommentInFront Atom=',GetAtom);
CommentStartPos:=FindNextComment(Src,p,StartPos);
if CommentStartPos>=StartPos then break;
p:=FindCommentEnd(Src,CommentStartPos,Scanner.NestedComments);
if p>StartPos then break;
CompareComment(CommentStartPos,p);
until false;
Result:=(FoundStartPos>=1);
CommentStart:=FoundStartPos;
CommentEnd:=FoundEndPos;
end;
function TPascalReaderTool.GetPasDocComments(const StartPos: TCodeXYPosition;
InvokeBuildTree: boolean; out ListOfPCodeXYPosition: TFPList): boolean;
var
CleanCursorPos: integer;
ANode: TCodeTreeNode;
begin
ListOfPCodeXYPosition:=nil;
Result:=false;
// parse source and find clean positions
if InvokeBuildTree then
BuildTreeAndGetCleanPos(StartPos,CleanCursorPos)
else
if CaretToCleanPos(StartPos,CleanCursorPos)<>0 then
exit;
ANode:=FindDeepestNodeAtPos(CleanCursorPos,true);
Result:=GetPasDocComments(ANode,ListOfPCodeXYPosition);
end;
function TPascalReaderTool.GetPasDocComments(Node: TCodeTreeNode;
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 <
//
// comment starting with $ or % are ignored
function CommentBelongsToPrior(CommentStart: integer): boolean;
var
p: Integer;
begin
//DebugLn(['CommentBelongsToPrior Comment=',dbgstr(copy(Src,CommentStart,20))]);
if (CommentStart<SrcLen) and (Src[CommentStart]='{')
and (Src[CommentStart+1]='<') then
Result:=true
else if (CommentStart+2<=SrcLen) and (Src[CommentStart]='(')
and (Src[CommentStart+1]='*') and (Src[CommentStart+2]='<') then
Result:=true
else if (CommentStart+2<=SrcLen) and (Src[CommentStart]='/')
and (Src[CommentStart+1]='/') and (Src[CommentStart+2]='<') then
Result:=true
else begin
p:=CommentStart-1;
while (p>=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;
pp: PChar;
begin
// read comments (start in front of node)
//DebugLn(['TPascalReaderTool.GetPasDocComments Scan Src=',copy(Src,StartPos,EndPos-StartPos)]);
if EndPos>SrcLen then EndPos:=SrcLen+1;
p:=FindLineEndOrCodeInFrontOfPosition(StartPos,true);
while p<EndPos do begin
p:=FindNextComment(Src,p,EndPos);
if (p>=EndPos) then break;
pp:=@Src[p];
if ((pp^='/') and (pp[1]='/') and (pp[2] in ['$','%']))
or ((pp^='{') and (pp[1] in ['$','%']))
or ((pp^='(') and (pp[1]='*') and (pp[2] in ['$','%']))
then
break;
//debugln(['TStandardCodeTool.GetPasDocComments Comment="',copy(Src,p,FindCommentEnd(Src,p,Scanner.NestedComments)-p),'"']);
if (p<StartPos) then begin
// comment in front of node
if not CommentBelongsToPrior(p) then
Add(p);
end else if (p<EndPos) then begin
// comment in the middle or behind
if CommentBelongsToPrior(p) then
Add(p);
end;
p:=FindCommentEnd(Src,p,Scanner.NestedComments);
end;
Result:=true;
end;
var
NextNode: TCodeTreeNode;
EndPos: LongInt;
TypeNode: TCodeTreeNode;
begin
ListOfPCodeXYPosition:=nil;
Result:=false;
if (Node=nil) then exit;
if (Node.Desc=ctnProcedureHead)
and (Node.Parent<>nil) and (Node.Parent.Desc=ctnProcedure) then
Node:=Node.Parent;
// add space behind node to scan range
NextNode:=Node.Next;
if NextNode<>nil then
EndPos:=NextNode.StartPos
else
EndPos:=Node.EndPos;
// scan range for comments
if not Scan(Node.StartPos,EndPos) then exit;
if Node.Desc in AllIdentifierDefinitions then begin
// scan behind type
// for example: i: integer; // comment
TypeNode:=FindTypeNodeOfDefinition(Node);
if TypeNode<>nil then begin
NextNode:=TypeNode.Next;
if NextNode<>nil then
EndPos:=NextNode.StartPos
else
EndPos:=Node.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.