lazarus/components/codetools/pascalreadertool.pas
2010-12-04 16:58:00 +00:00

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