mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-02 19:23:40 +02:00
3493 lines
111 KiB
ObjectPascal
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.
|
|
|