/components/codetools/pascalreadertool.pas

http://github.com/graemeg/lazarus · Pascal · 3764 lines · 3238 code · 237 blank · 289 comment · 762 complexity · 9588f51c2b0af8cf12bed6b33ebc8a5b MD5 · raw file

Large files are truncated click here to view the full file

  1. {
  2. ***************************************************************************
  3. * *
  4. * This source is free software; you can redistribute it and/or modify *
  5. * it under the terms of the GNU General Public License as published by *
  6. * the Free Software Foundation; either version 2 of the License, or *
  7. * (at your option) any later version. *
  8. * *
  9. * This code is distributed in the hope that it will be useful, but *
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of *
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
  12. * General Public License for more details. *
  13. * *
  14. * A copy of the GNU General Public License is available on the World *
  15. * Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
  16. * obtain it by writing to the Free Software Foundation, *
  17. * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
  18. * *
  19. ***************************************************************************
  20. Author: Mattias Gaertner
  21. Abstract:
  22. TPascalReaderTool enhances TPascalParserTool.
  23. This tool provides a lot of useful functions to read the output of the
  24. TPascalParserTool.
  25. }
  26. unit PascalReaderTool;
  27. {$ifdef FPC}{$mode objfpc}{$endif}{$H+}
  28. interface
  29. {$I codetools.inc}
  30. uses
  31. {$IFDEF MEM_CHECK}
  32. MemCheck,
  33. {$ENDIF}
  34. Classes, SysUtils, FileProcs, CodeToolsStrConsts, CodeTree, CodeCache,
  35. CodeAtom, CustomCodeTool, PascalParserTool, KeywordFuncLists, BasicCodeTools,
  36. LinkScanner, AVL_Tree, LazFileUtils, LazDbgLog;
  37. type
  38. TPascalHintModifier = (
  39. phmDeprecated,
  40. phmPlatform,
  41. phmLibrary,
  42. phmUnimplemented,
  43. phmExperimental
  44. );
  45. TPascalHintModifiers = set of TPascalHintModifier;
  46. TEPRIRange = (
  47. epriInCode,
  48. epriInComment,
  49. epriInDirective
  50. );
  51. //the scope groups of pascal methods.
  52. //please note that Destructor is principally a method and thus is not listed here -> you cannot define "procedure Destroy;" and "destructor Destroy" in one class
  53. TPascalMethodGroup = (mgMethod, mgConstructor, mgClassConstructor, mgClassDestructor, mgClassOperator);
  54. TPascalMethodHeader = record
  55. Name, ResultType: string;
  56. Group: TPascalMethodGroup;
  57. end;
  58. TClassSectionVisibility = (
  59. csvEverything,//same class same unit
  60. csvPrivateAndHigher,//same unit different class
  61. csvProtectedAndHigher,//ancestor class different unit
  62. csvPublicAndHigher);//other class other unit
  63. TOnEachPRIdentifier = procedure(Sender: TPascalParserTool;
  64. IdentifierCleanPos: integer; Range: TEPRIRange;
  65. Node: TCodeTreeNode; Data: Pointer; var Abort: boolean) of object;
  66. { TPascalReaderTool }
  67. TPascalReaderTool = class(TPascalParserTool)
  68. protected
  69. CachedSourceName: string;
  70. procedure RaiseStrConstExpected;
  71. public
  72. // comments
  73. function CleanPosIsInComment(CleanPos, CleanCodePosInFront: integer;
  74. out CommentStart, CommentEnd: integer;
  75. OuterCommentBounds: boolean = true): boolean;
  76. // general extraction
  77. function ExtractNode(ANode: TCodeTreeNode;
  78. Attr: TProcHeadAttributes): string;
  79. function ExtractCode(StartPos, EndPos: integer;
  80. Attr: TProcHeadAttributes): string;
  81. function ExtractBrackets(BracketStartPos: integer;
  82. Attr: TProcHeadAttributes): string;
  83. function ExtractIdentifierWithPoints(StartPos: integer;
  84. ExceptionOnError: boolean): string;
  85. function ExtractIdentCharsFromStringConstant(
  86. StartPos, MinPos, MaxPos, MaxLen: integer): string;
  87. function ReadStringConstantValue(StartPos: integer): string;
  88. function GetNodeIdentifier(Node: TCodeTreeNode): PChar;
  89. function GetHintModifiers(Node: TCodeTreeNode): TPascalHintModifiers;
  90. procedure ForEachIdentifierInCleanSrc(StartPos, EndPos: integer;
  91. SkipComments: boolean; Node: TCodeTreeNode;
  92. const OnIdentifier: TOnEachPRIdentifier; Data: pointer;
  93. var Abort: boolean); // range in clean source
  94. procedure ForEachIdentifierInNode(Node: TCodeTreeNode; SkipComments: boolean;
  95. const OnIdentifier: TOnEachPRIdentifier; Data: Pointer; var Abort: boolean); // node and child nodes
  96. procedure ForEachIdentifier(SkipComments: boolean;
  97. const OnIdentifier: TOnEachPRIdentifier; Data: Pointer); // whole unit/program
  98. // properties
  99. function ExtractPropType(PropNode: TCodeTreeNode;
  100. InUpperCase, EmptyIfIndexed: boolean): string;
  101. function MoveCursorToPropType(PropNode: TCodeTreeNode): boolean;
  102. function MoveCursorToPropName(PropNode: TCodeTreeNode): boolean;
  103. procedure MoveCursorBehindPropName(PropNode: TCodeTreeNode);
  104. function ExtractPropName(PropNode: TCodeTreeNode;
  105. InUpperCase: boolean): string;
  106. function ExtractProperty(PropNode: TCodeTreeNode;
  107. Attr: TProcHeadAttributes): string;
  108. function GetPropertyNameIdentifier(PropNode: TCodeTreeNode): PChar;
  109. function GetPropertyTypeIdentifier(PropNode: TCodeTreeNode): PChar;
  110. function PositionInPropertyName(PropNode: TCodeTreeNode;
  111. CleanPos: integer): boolean;
  112. function PropertyIsDefault(PropertyNode: TCodeTreeNode): boolean;
  113. function PropertyNodeHasParamList(PropNode: TCodeTreeNode): boolean;
  114. function PropNodeIsTypeLess(PropNode: TCodeTreeNode): boolean;
  115. function PropertyHasSpecifier(PropNode: TCodeTreeNode;
  116. UpperKeyword: string; ExceptionOnNotFound: boolean = true): boolean;
  117. // procs
  118. function ExtractProcName(ProcNode: TCodeTreeNode;
  119. Attr: TProcHeadAttributes): string;
  120. function ExtractProcHead(ProcNode: TCodeTreeNode;
  121. Attr: TProcHeadAttributes): string;
  122. function ExtractProcHeadWithGroup(ProcNode: TCodeTreeNode;
  123. Attr: TProcHeadAttributes): TPascalMethodHeader;
  124. function ExtractProcedureHeader(CursorPos: TCodeXYPosition;
  125. Attributes: TProcHeadAttributes; var ProcHead: string): boolean;
  126. function ExtractClassNameOfProcNode(ProcNode: TCodeTreeNode;
  127. AddParentClasses: boolean = true): string;
  128. function ProcNodeHasSpecifier(ProcNode: TCodeTreeNode;
  129. ProcSpec: TProcedureSpecifier): boolean;
  130. function GetProcNameIdentifier(ProcNode: TCodeTreeNode): PChar;
  131. function FindProcNode(StartNode: TCodeTreeNode; const AProcHead: string;
  132. AProcSpecType: TPascalMethodGroup;
  133. Attr: TProcHeadAttributes; Visibility: TClassSectionVisibility = csvEverything): TCodeTreeNode; overload;
  134. function FindProcNode(StartNode: TCodeTreeNode; const AProcHead: TPascalMethodHeader;
  135. Attr: TProcHeadAttributes; Visibility: TClassSectionVisibility = csvEverything): TCodeTreeNode; overload;
  136. function FindCorrespondingProcNode(ProcNode: TCodeTreeNode;
  137. Attr: TProcHeadAttributes = [phpWithoutClassKeyword,phpWithoutClassName]
  138. ): TCodeTreeNode;
  139. function FindCorrespondingProcParamNode(ProcParamNode: TCodeTreeNode;
  140. Attr: TProcHeadAttributes = [phpInUpperCase,phpWithoutClassName,phpWithVarModifiers]
  141. ): TCodeTreeNode;
  142. function FindProcBody(ProcNode: TCodeTreeNode): TCodeTreeNode;
  143. function ProcBodyIsEmpty(ProcNode: TCodeTreeNode): boolean;
  144. function ExtractProcedureGroup(ProcNode: TCodeTreeNode): TPascalMethodGroup;
  145. function ExtractFuncResultType(ProcNode: TCodeTreeNode;
  146. Attr: TProcHeadAttributes): string;
  147. procedure MoveCursorToFirstProcSpecifier(ProcNode: TCodeTreeNode);
  148. function MoveCursorToProcSpecifier(ProcNode: TCodeTreeNode;
  149. ProcSpec: TProcedureSpecifier): boolean;
  150. procedure MoveCursorToProcName(ProcNode: TCodeTreeNode;
  151. SkipClassName: boolean);
  152. procedure MoveCursorBehindProcName(ProcNode: TCodeTreeNode);
  153. function PositionInProcName(ProcNode: TCodeTreeNode;
  154. SkipClassName: boolean; CleanPos: integer): boolean;
  155. function PositionInFuncResultName(ProcNode: TCodeTreeNode;
  156. CleanPos: integer): boolean;
  157. function ProcNodeHasParamList(ProcNode: TCodeTreeNode): boolean;
  158. function ProcNodeHasOfObject(ProcNode: TCodeTreeNode): boolean;
  159. function GetProcParamList(ProcNode: TCodeTreeNode;
  160. Parse: boolean = true): TCodeTreeNode;
  161. function NodeIsInAMethod(Node: TCodeTreeNode): boolean;
  162. function NodeIsMethodBody(ProcNode: TCodeTreeNode): boolean;
  163. function GetMethodOfBody(Node: TCodeTreeNode): TCodeTreeNode;
  164. function NodeIsFunction(ProcNode: TCodeTreeNode): boolean;
  165. function NodeIsClassConstructorOrDestructor(ProcNode: TCodeTreeNode): boolean;
  166. function NodeIsConstructor(ProcNode: TCodeTreeNode): boolean;
  167. function NodeIsDestructor(ProcNode: TCodeTreeNode): boolean;
  168. function NodeIsForwardProc(ProcNode: TCodeTreeNode): boolean;
  169. function NodeIsOperator(ProcNode: TCodeTreeNode): boolean;
  170. function NodeIsResultIdentifier(Node: TCodeTreeNode): boolean;
  171. function NodeIsResultType(Node: TCodeTreeNode): boolean;
  172. // classes
  173. function ExtractClassName(Node: TCodeTreeNode;
  174. InUpperCase: boolean; WithParents: boolean = true;
  175. WithGenericParams: boolean = false): string;
  176. function ExtractClassPath(Node: TCodeTreeNode): string;
  177. function ExtractClassInheritance(ClassNode: TCodeTreeNode;
  178. Attr: TProcHeadAttributes): string;
  179. function FindClassNode(StartNode: TCodeTreeNode;
  180. const AClassName: string; // nested: A.B
  181. IgnoreForwards, IgnoreNonForwards: boolean): TCodeTreeNode;
  182. function FindClassNodeBackwards(StartNode: TCodeTreeNode;
  183. const AClassName: string;
  184. IgnoreForwards, IgnoreNonForwards: boolean): TCodeTreeNode;
  185. function FindNestedClass(RootClassNode: TCodeTreeNode;
  186. AClassName: PChar; SkipFirst: boolean): TCodeTreeNode;
  187. function FindClassNode(CursorNode: TCodeTreeNode): TCodeTreeNode;
  188. function FindClassNodeForMethodBody(ProcNode: TCodeTreeNode;
  189. IgnoreForwards, IgnoreNonForwards: boolean): TCodeTreeNode;
  190. function FindClassOrInterfaceNode(CursorNode: TCodeTreeNode;
  191. FindClassOfMethod: boolean = false): TCodeTreeNode;
  192. function FindClassSection(ClassNode: TCodeTreeNode;
  193. NodeDesc: TCodeTreeNodeDesc): TCodeTreeNode;
  194. function FindLastClassSection(ClassNode: TCodeTreeNode;
  195. NodeDesc: TCodeTreeNodeDesc): TCodeTreeNode;
  196. function GetClassVisibility(Node: TCodeTreeNode): TCodeTreeNodeDesc;
  197. function FindClassNodeInInterface(const AClassName: string;
  198. IgnoreForwards, IgnoreNonForwards, ErrorOnNotFound: boolean): TCodeTreeNode;
  199. function FindClassNodeInUnit(const AClassName: string;
  200. IgnoreForwards, IgnoreNonForwards, IgnoreImplementation,
  201. ErrorOnNotFound: boolean): TCodeTreeNode;
  202. function FindFirstIdentNodeInClass(ClassNode: TCodeTreeNode): TCodeTreeNode;
  203. function FindLastIdentNodeInClass(ClassNode: TCodeTreeNode): TCodeTreeNode;
  204. function FindNextIdentNodeInClass(Node: TCodeTreeNode): TCodeTreeNode;
  205. function FindPriorIdentNodeInClass(Node: TCodeTreeNode): TCodeTreeNode;
  206. function ClassSectionNodeStartsWithWord(ANode: TCodeTreeNode): boolean;
  207. function IsClassNode(Node: TCodeTreeNode): boolean; // class, not object
  208. function FindInheritanceNode(ClassNode: TCodeTreeNode): TCodeTreeNode;
  209. function FindHelperForNode(HelperNode: TCodeTreeNode): TCodeTreeNode;
  210. function IdentNodeIsInVisibleClassSection(Node: TCodeTreeNode; Visibility: TClassSectionVisibility): Boolean;
  211. // records
  212. function ExtractRecordCaseType(RecordCaseNode: TCodeTreeNode): string;
  213. // variables, types
  214. function FindVarNode(StartNode: TCodeTreeNode;
  215. const UpperVarName: string;
  216. Visibility: TClassSectionVisibility = csvEverything): TCodeTreeNode;
  217. function FindTypeNodeOfDefinition(
  218. DefinitionNode: TCodeTreeNode): TCodeTreeNode;
  219. function NodeIsPartOfTypeDefinition(ANode: TCodeTreeNode): boolean;
  220. function ExtractDefinitionNodeType(DefinitionNode: TCodeTreeNode): string;
  221. function ExtractDefinitionName(DefinitionNode: TCodeTreeNode): string;
  222. function FindDefinitionNameNode(DefinitionNode: TCodeTreeNode): TCodeTreeNode;
  223. function PositionInDefinitionName(DefinitionNode: TCodeTreeNode;
  224. CleanPos: integer): boolean;
  225. function MoveCursorToParameterSpecifier(DefinitionNode: TCodeTreeNode
  226. ): boolean;
  227. function GetFirstGroupVarNode(VarNode: TCodeTreeNode): TCodeTreeNode;
  228. function NodeIsIdentifierInInterface(Node: TCodeTreeNode): boolean;
  229. function NodeCanHaveForwardType(TypeNode: TCodeTreeNode): boolean;
  230. function NodeIsForwardType(TypeNode: TCodeTreeNode): boolean;
  231. function FindForwardTypeNode(TypeNode: TCodeTreeNode;
  232. SearchFirst: boolean): TCodeTreeNode;
  233. function FindTypeOfForwardNode(TypeNode: TCodeTreeNode): TCodeTreeNode;
  234. function FindEndOfWithExpr(WithVarNode: TCodeTreeNode): integer;
  235. function ExtractWithBlockExpression(WithVarNode: TCodeTreeNode; Attr: TProcHeadAttributes = []): string;
  236. function FindWithBlockStatement(WithVarNode: TCodeTreeNode): TCodeTreeNode;
  237. // arrays
  238. function ExtractArrayRange(ArrayNode: TCodeTreeNode;
  239. Attr: TProcHeadAttributes): string;
  240. // module sections
  241. function ExtractSourceName: string;
  242. function GetSourceNamePos(out NamePos: TAtomPosition): boolean;
  243. function GetSourceName(DoBuildTree: boolean = true): string;
  244. function GetSourceType: TCodeTreeNodeDesc;
  245. function PositionInSourceName(CleanPos: integer): boolean;
  246. // uses sections
  247. procedure MoveCursorToUsesStart(UsesNode: TCodeTreeNode);
  248. procedure MoveCursorToUsesEnd(UsesNode: TCodeTreeNode);
  249. function ReadNextUsedUnit(out UnitNameRange, InAtom: TAtomPosition;
  250. SyntaxExceptions: boolean = true): boolean;
  251. procedure ReadPriorUsedUnit(out UnitNameRange, InAtom: TAtomPosition);
  252. function ExtractUsedUnitNameAtCursor(InFilename: PAnsiString = nil): string;
  253. function ExtractUsedUnitName(UseUnitNode: TCodeTreeNode;
  254. InFilename: PAnsiString = nil): string;
  255. function ReadAndCompareUsedUnit(const AnUnitName: string): boolean;
  256. // comments
  257. function FindCommentInFront(const StartPos: TCodeXYPosition;
  258. const CommentText: string; InvokeBuildTree, SearchInParentNode,
  259. WithCommentBounds, CaseSensitive, IgnoreSpaces,
  260. CompareOnlyStart: boolean;
  261. out CommentStart, CommentEnd: TCodeXYPosition): boolean;
  262. function FindCommentInFront(StartPos: integer;
  263. const CommentText: string; SearchInParentNode,
  264. WithCommentBounds, CaseSensitive, IgnoreSpaces,
  265. CompareOnlyStart: boolean;
  266. out CommentStart, CommentEnd: integer): boolean;
  267. function GetPasDocComments(const StartPos: TCodeXYPosition;
  268. InvokeBuildTree: boolean;
  269. out ListOfPCodeXYPosition: TFPList): boolean;
  270. function GetPasDocComments(Node: TCodeTreeNode;
  271. out ListOfPCodeXYPosition: TFPList): boolean;
  272. procedure CalcMemSize(Stats: TCTMemStats); override;
  273. end;
  274. function CompareMethodHeaders(
  275. const Method1Name: string; Method1Group: TPascalMethodGroup; const Method1ResultType: string;
  276. const Method2Name: string; Method2Group: TPascalMethodGroup; const Method2ResultType: string): Integer; overload;
  277. function CompareMethodHeaders(const Method1Head: TPascalMethodHeader; const Method2Head: TPascalMethodHeader): Integer; overload;
  278. function SameMethodHeaders(
  279. const Method1Name: string; Method1Group: TPascalMethodGroup; const Method1ResultType: string;
  280. const Method2Name: string; Method2Group: TPascalMethodGroup; const Method2ResultType: string): Boolean; overload;
  281. function SameMethodHeaders(const Method1Head: TPascalMethodHeader; const Method2Head: TPascalMethodHeader): Boolean; overload;
  282. function CompareCodeTreeNodeExtMethodHeaders(NodeData1, NodeData2: pointer): integer;
  283. implementation
  284. function CompareMethodHeaders(const Method1Name: string;
  285. Method1Group: TPascalMethodGroup; const Method1ResultType: string;
  286. const Method2Name: string; Method2Group: TPascalMethodGroup;
  287. const Method2ResultType: string): Integer;
  288. begin
  289. Result := (Ord(Method1Group) - Ord(Method2Group));
  290. if Result <> 0 then exit;
  291. Result := CompareTextIgnoringSpace(Method1Name,Method2Name,false);
  292. if Result <> 0 then exit;
  293. if Method1Group=mgClassOperator then
  294. Result := CompareTextIgnoringSpace(Method1ResultType,Method2ResultType,false);
  295. end;
  296. function CompareMethodHeaders(const Method1Head: TPascalMethodHeader;
  297. const Method2Head: TPascalMethodHeader): Integer;
  298. begin
  299. Result := CompareMethodHeaders(
  300. Method1Head.Name, Method1Head.Group, Method1Head.ResultType,
  301. Method2Head.Name, Method2Head.Group, Method2Head.ResultType);
  302. end;
  303. function SameMethodHeaders(const Method1Name: string;
  304. Method1Group: TPascalMethodGroup; const Method1ResultType: string;
  305. const Method2Name: string; Method2Group: TPascalMethodGroup;
  306. const Method2ResultType: string): Boolean;
  307. begin
  308. Result := CompareMethodHeaders(
  309. Method1Name, Method1Group, Method1ResultType,
  310. Method2Name, Method2Group, Method2ResultType) = 0;
  311. end;
  312. function SameMethodHeaders(const Method1Head: TPascalMethodHeader;
  313. const Method2Head: TPascalMethodHeader): Boolean;
  314. begin
  315. Result := CompareMethodHeaders(Method1Head, Method2Head) = 0;
  316. end;
  317. function CompareCodeTreeNodeExtMethodHeaders(NodeData1, NodeData2: pointer): integer;
  318. var
  319. NodeExt1: TCodeTreeNodeExtension absolute NodeData1;
  320. NodeExt2: TCodeTreeNodeExtension absolute NodeData2;
  321. begin
  322. Result := CompareMethodHeaders(
  323. NodeExt1.Txt,TPascalMethodGroup(NodeExt1.Flags),NodeExt1.ExtTxt4,
  324. NodeExt2.Txt,TPascalMethodGroup(NodeExt2.Flags),NodeExt2.ExtTxt4);
  325. end;
  326. { TPascalReaderTool }
  327. procedure TPascalReaderTool.RaiseStrConstExpected;
  328. begin
  329. RaiseExceptionFmt(ctsStrExpectedButAtomFound,[ctsStringConstant,GetAtom]);
  330. end;
  331. function TPascalReaderTool.CleanPosIsInComment(CleanPos,
  332. CleanCodePosInFront: integer; out CommentStart, CommentEnd: integer;
  333. OuterCommentBounds: boolean): boolean;
  334. var CommentLvl, CurCommentPos: integer;
  335. CurEnd: Integer;
  336. CurCommentInnerEnd: Integer;
  337. begin
  338. Result:=false;
  339. CommentStart:=0;
  340. CommentEnd:=0;
  341. if CleanPos>SrcLen then exit;
  342. if CleanCodePosInFront>CleanPos then
  343. RaiseException(
  344. 'TPascalReaderTool.CleanPosIsInComment CleanCodePosInFront>CleanPos');
  345. MoveCursorToCleanPos(CleanCodePosInFront);
  346. repeat
  347. ReadNextAtom;
  348. if CurPos.StartPos>CleanPos then begin
  349. //DebugLn(['TPascalReaderTool.CleanPosIsInComment ',GetATom,' StartPos=',CurPos.StartPos,' CleanPos=',CleanPos]);
  350. // CleanPos between two atoms -> parse space between for comments
  351. if LastAtoms.Count>0 then
  352. CommentStart:=LastAtoms.GetValueAt(0).EndPos
  353. else
  354. CommentStart:=CleanCodePosInFront;
  355. CurEnd:=CurPos.StartPos;
  356. if CurEnd>SrcLen then CurEnd:=SrcLen+1;
  357. while CommentStart<CurEnd do begin
  358. if IsCommentStartChar[Src[CommentStart]] then begin
  359. CurCommentPos:=CommentStart;
  360. CurCommentInnerEnd:=CurEnd;
  361. case Src[CommentStart] of
  362. '{':
  363. begin
  364. inc(CurCommentPos);
  365. if (CurCommentPos<=SrcLen) and (Src[CurCommentPos]=#3) then begin
  366. // codetools skip comment
  367. inc(CurCommentPos);
  368. if not OuterCommentBounds then CommentStart:=CurCommentPos;
  369. while (CurCommentPos<CurEnd) do begin
  370. if (Src[CurCommentPos]=#3)
  371. and (CurCommentPos+1<CurEnd) and (Src[CurCommentPos+1]='}')
  372. then begin
  373. CurCommentInnerEnd:=CurCommentPos;
  374. inc(CurCommentPos,2);
  375. break;
  376. end;
  377. inc(CurCommentPos);
  378. end;
  379. end else begin
  380. // pascal comment
  381. if not OuterCommentBounds then CommentStart:=CurCommentPos;
  382. CommentLvl:=1;
  383. while (CurCommentPos<CurEnd) do begin
  384. case Src[CurCommentPos] of
  385. '{': if Scanner.NestedComments then inc(CommentLvl);
  386. '}':
  387. begin
  388. dec(CommentLvl);
  389. if (CommentLvl=0) then begin
  390. CurCommentInnerEnd:=CurCommentPos;
  391. inc(CurCommentPos);
  392. break;
  393. end;
  394. end;
  395. end;
  396. inc(CurCommentPos);
  397. end;
  398. end;
  399. end;
  400. '/': // Delphi comment
  401. if (CurCommentPos<SrcLen) and (Src[CurCommentPos+1]='/') then
  402. begin
  403. inc(CurCommentPos,2);
  404. if not OuterCommentBounds then CommentStart:=CurCommentPos;
  405. while (CurCommentPos<CurEnd)
  406. and (not (Src[CurCommentPos] in [#10,#13])) do
  407. inc(CurCommentPos);
  408. CurCommentInnerEnd:=CurCommentPos;
  409. inc(CurCommentPos);
  410. if (CurCommentPos<CurEnd)
  411. and (Src[CurCommentPos] in [#10,#13])
  412. and (Src[CurCommentPos-1]<>Src[CurCommentPos]) then
  413. inc(CurCommentPos);
  414. end else
  415. break;
  416. '(': // Turbo pascal comment
  417. if (CurCommentPos<SrcLen) and (Src[CurCommentPos+1]='*') then
  418. begin
  419. inc(CurCommentPos,2);
  420. if not OuterCommentBounds then CommentStart:=CurCommentPos;
  421. while (CurCommentPos<CurEnd) do begin
  422. if (Src[CurCommentPos]='*') and (CurCommentPos+1<CurEnd)
  423. and (Src[CurCommentPos+1]=')') then
  424. begin
  425. CurCommentInnerEnd:=CurCommentPos;
  426. inc(CurCommentPos,2);
  427. break;
  428. end;
  429. inc(CurCommentPos);
  430. end;
  431. end else
  432. break;
  433. end;
  434. if (CurCommentPos>CommentStart) and (CleanPos<CurCommentPos) then
  435. begin
  436. // CleanPos in comment
  437. if OuterCommentBounds then
  438. CommentEnd:=CurCommentPos
  439. else
  440. CommentEnd:=CurCommentInnerEnd;
  441. exit(true);
  442. end;
  443. CommentStart:=CurCommentPos;
  444. end else if IsSpaceChar[Src[CommentStart]] then begin
  445. repeat
  446. inc(CommentStart);
  447. until (CommentStart>=CommentEnd)
  448. or (not (IsSpaceChar[Src[CommentStart]]));
  449. end else begin
  450. break;
  451. end;
  452. end;
  453. // CleanPos not in a comment
  454. exit;
  455. end else if CurPos.EndPos>CleanPos then begin
  456. // CleanPos not in a comment
  457. exit;
  458. end;
  459. CleanCodePosInFront:=CurPos.EndPos;
  460. until CurPos.StartPos>=SrcLen;
  461. end;
  462. function TPascalReaderTool.ExtractPropType(PropNode: TCodeTreeNode;
  463. InUpperCase, EmptyIfIndexed: boolean): string;
  464. begin
  465. Result:='';
  466. if (PropNode=nil)
  467. or ((PropNode.Desc<>ctnProperty) and (PropNode.Desc<>ctnGlobalProperty)) then
  468. exit;
  469. MoveCursorToNodeStart(PropNode);
  470. ReadNextAtom;
  471. if (PropNode.Desc=ctnProperty) then begin
  472. if UpAtomIs('CLASS') then ReadNextAtom;
  473. if (not UpAtomIs('PROPERTY')) then exit;
  474. ReadNextAtom;
  475. end;
  476. AtomIsIdentifierE;
  477. ReadNextAtom;
  478. if CurPos.Flag=cafEdgedBracketOpen then begin
  479. if EmptyIfIndexed then exit;
  480. ReadTilBracketClose(true);
  481. ReadNextAtom;
  482. end;
  483. if CurPos.Flag in [cafSemicolon,cafEND] then exit;
  484. if not (CurPos.Flag=cafColon) then
  485. RaiseExceptionFmt(ctsStrExpectedButAtomFound,[':',GetAtom]);
  486. ReadNextAtom;
  487. AtomIsIdentifierE;
  488. if InUpperCase then
  489. Result:=GetUpAtom
  490. else
  491. Result:=GetAtom;
  492. end;
  493. function TPascalReaderTool.ExtractProcName(ProcNode: TCodeTreeNode;
  494. Attr: TProcHeadAttributes): string;
  495. var
  496. ProcHeadNode: TCodeTreeNode;
  497. Part: String;
  498. HasClassName: Boolean;
  499. begin
  500. Result:='';
  501. if [phpWithoutClassName,phpWithoutName]*Attr=
  502. [phpWithoutClassName,phpWithoutName]
  503. then
  504. exit;
  505. while (ProcNode<>nil) and (ProcNode.Desc<>ctnProcedure) do
  506. ProcNode:=ProcNode.Parent;
  507. if ProcNode=nil then exit;
  508. ProcHeadNode:=ProcNode.FirstChild;
  509. if (ProcHeadNode=nil) or (ProcHeadNode.StartPos<1) then exit;
  510. MoveCursorToNodeStart(ProcHeadNode);
  511. HasClassName:=false;
  512. repeat
  513. ReadNextAtom;
  514. if not AtomIsIdentifier then break;
  515. if phpInUpperCase in Attr then
  516. Part:=GetUpAtom
  517. else
  518. Part:=GetAtom;
  519. ReadNextAtom;
  520. if (CurPos.Flag<>cafPoint) then begin
  521. // end of method identifier is the proc name
  522. if phpWithoutName in Attr then break;
  523. if Result<>'' then Result:=Result+'.';
  524. Result:=Result+Part;
  525. break;
  526. end;
  527. if not (phpWithoutClassName in Attr) then begin
  528. // in front of . is class name
  529. if Result<>'' then Result:=Result+'.';
  530. Result:=Result+Part;
  531. HasClassName:=true;
  532. end;
  533. until false;
  534. if (not HasClassName)
  535. and ([phpWithoutClassName,phpAddClassName]*Attr=[phpAddClassName]) then begin
  536. Part:=ExtractClassName(ProcNode,false,true);
  537. if Part<>'' then
  538. Result:=Part+'.'+Result;
  539. end;
  540. end;
  541. function TPascalReaderTool.ExtractProcHead(ProcNode: TCodeTreeNode;
  542. Attr: TProcHeadAttributes): string;
  543. var
  544. TheClassName, s: string;
  545. IsClassName, IsProcType: boolean;
  546. IsProcedure: Boolean;
  547. IsFunction: Boolean;
  548. IsOperator: Boolean;
  549. EndPos: Integer;
  550. ParentNode: TCodeTreeNode;
  551. const
  552. SemiColon : char = ';';
  553. procedure PrependName(const Prepend: string; var aPath: string);
  554. begin
  555. if Prepend='' then exit;
  556. if aPath<>'' then
  557. aPath:=Prepend+'.'+aPath
  558. else
  559. aPath:=Prepend;
  560. end;
  561. begin
  562. Result:='';
  563. ExtractProcHeadPos:=phepNone;
  564. if (ProcNode=nil) or (ProcNode.StartPos<1) then exit;
  565. if ProcNode.Desc=ctnProcedureHead then begin
  566. ProcNode:=ProcNode.Parent;
  567. if ProcNode=nil then exit;
  568. end;
  569. if ProcNode.Desc=ctnProcedure then
  570. IsProcType:=false
  571. else if ProcNode.Desc=ctnProcedureType then
  572. IsProcType:=true
  573. else
  574. exit;
  575. TheClassName:='';
  576. if (phpAddParentProcs in Attr) and (ProcNode.Parent.Desc=ctnProcedure) then begin
  577. // local proc
  578. ParentNode:=ProcNode.Parent;
  579. while ParentNode.Desc=ctnProcedure do begin
  580. PrependName(ExtractProcName(ParentNode,Attr*[phpInUpperCase]),TheClassName);
  581. ParentNode:=ParentNode.Parent;
  582. end;
  583. end;
  584. // build full class name
  585. if ([phpAddClassname,phpWithoutClassName]*Attr=[phpAddClassName]) then
  586. PrependName(ExtractClassName(ProcNode,phpInUpperCase in Attr,true),TheClassName);
  587. // reparse the clean source
  588. InitExtraction;
  589. MoveCursorToNodeStart(ProcNode);
  590. // parse procedure head = start + name + parameterlist + result type ;
  591. ExtractNextAtom(false,Attr);
  592. // read procedure start keyword
  593. if (UpAtomIs('CLASS') or UpAtomIs('STATIC')) then
  594. ExtractNextAtom((phpWithStart in Attr)
  595. and not (phpWithoutClassKeyword in Attr),Attr);
  596. IsProcedure:=UpAtomIs('PROCEDURE');
  597. IsFunction:=(not IsProcedure) and UpAtomIs('FUNCTION');
  598. IsOperator:=(not IsProcedure) and (not IsFunction) and UpAtomIs('OPERATOR');
  599. if IsProcedure or IsFunction or IsOperator
  600. or (UpAtomIs('CONSTRUCTOR')) or (UpAtomIs('DESTRUCTOR'))
  601. then
  602. ExtractNextAtom(phpWithStart in Attr,Attr)
  603. else
  604. exit;
  605. ExtractProcHeadPos:=phepStart;
  606. if not IsProcType then begin
  607. // read name
  608. if ((not IsOperator)
  609. or (not WordIsCustomOperator.DoItCaseInsensitive(Src,CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)))
  610. and (not AtomIsIdentifier) then exit;
  611. if TheClassName<>'' then begin
  612. s:=TheClassName+'.';
  613. if phpInUpperCase in Attr then s:=UpperCaseStr(s);
  614. if ExtractStreamEndIsIdentChar then
  615. s:=' '+s;
  616. ExtractMemStream.Write(s[1],length(s));
  617. end;
  618. if [phpWithoutClassName,phpWithoutName]*Attr=[] then begin
  619. // read classname and name
  620. repeat
  621. ExtractNextAtom(true,Attr);
  622. if Scanner.CompilerMode = cmDELPHI then
  623. begin
  624. // delphi generics
  625. if AtomIsChar('<') then
  626. begin
  627. while not AtomIsChar('>') and (CurPos.EndPos < SrcLen) do
  628. ExtractNextAtom(not (phpWithoutGenericParams in Attr),Attr);
  629. ExtractNextAtom(not (phpWithoutGenericParams in Attr),Attr);
  630. end;
  631. end;
  632. if CurPos.Flag<>cafPoint then break;
  633. ExtractNextAtom(true,Attr);
  634. if ((not IsOperator)
  635. or (not WordIsCustomOperator.DoItCaseInsensitive(Src,CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)))
  636. and (not AtomIsIdentifier) then exit;
  637. until false;
  638. end else begin
  639. // read only part of name
  640. repeat
  641. ReadNextAtom;
  642. if (Scanner.CompilerMode = cmDELPHI) and AtomIsChar('<') then
  643. begin
  644. while not AtomIsChar('>') and (CurPos.EndPos < SrcLen) do
  645. ReadNextAtom;
  646. ReadNextAtom;
  647. end;
  648. IsClassName:=(CurPos.Flag=cafPoint);
  649. UndoReadNextAtom;
  650. if IsClassName then begin
  651. // read class name
  652. ExtractNextAtom(not (phpWithoutClassName in Attr),Attr);
  653. // read '.'
  654. ExtractNextAtom(not (phpWithoutClassName in Attr),Attr);
  655. if ((not IsOperator)
  656. or (not WordIsCustomOperator.DoItCaseInsensitive(Src,CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)))
  657. and (not AtomIsIdentifier) then exit;
  658. end else begin
  659. // read name
  660. ExtractNextAtom(not (phpWithoutName in Attr),Attr);
  661. break;
  662. end;
  663. until false;
  664. end;
  665. ExtractProcHeadPos:=phepName;
  666. end;
  667. // read parameter list
  668. if (CurPos.Flag=cafRoundBracketOpen) then
  669. ReadParamList(false,true,Attr);
  670. ExtractProcHeadPos:=phepParamList;
  671. if IsOperator and (CurPos.Flag=cafWord) then begin
  672. // read operator result name
  673. ExtractNextAtom([phpWithParameterNames,phpWithResultType]*Attr
  674. =[phpWithParameterNames,phpWithResultType],Attr);
  675. end;
  676. // read result type
  677. if (CurPos.Flag=cafColon) then begin
  678. ExtractNextAtom(phpWithResultType in Attr,Attr);
  679. if not AtomIsIdentifier then exit;
  680. ExtractNextAtom(phpWithResultType in Attr,Attr);
  681. if CurPos.Flag=cafPoint then begin
  682. ExtractNextAtom(phpWithResultType in Attr,Attr);
  683. if not AtomIsIdentifier then exit;
  684. ExtractNextAtom(phpWithResultType in Attr,Attr);
  685. end;
  686. ExtractProcHeadPos:=phepResultType;
  687. end;
  688. // read 'of object'
  689. if UpAtomIs('OF') then begin
  690. if IsProcType then begin
  691. ExtractNextAtom(phpWithOfObject in Attr,Attr);
  692. if not UpAtomIs('OBJECT') then exit;
  693. ExtractNextAtom(phpWithOfObject in Attr,Attr);
  694. end;
  695. end;
  696. // read semicolon
  697. if CurPos.Flag=cafSemicolon then
  698. ExtractNextAtom(not (phpWithoutSemicolon in Attr),Attr);
  699. // read specifiers
  700. if [phpWithCallingSpecs,phpWithProcModifiers]*Attr<>[] then begin
  701. if ProcNode.FirstChild<>nil then
  702. EndPos:=ProcNode.FirstChild.EndPos
  703. else
  704. EndPos:=SrcLen+1;
  705. while (CurPos.StartPos<EndPos) do begin
  706. if CurPos.Flag=cafSemicolon then begin
  707. ExtractNextAtom(phpWithProcModifiers in Attr,Attr);
  708. end else begin
  709. if IsKeyWordCallingConvention.DoIdentifier(@Src[CurPos.StartPos])
  710. then begin
  711. ExtractNextAtom([phpWithCallingSpecs,phpWithProcModifiers]*Attr<>[],
  712. Attr);
  713. if not (phpWithProcModifiers in Attr) then
  714. ExtractMemStream.Write(SemiColon,1);
  715. end
  716. else if (CurPos.Flag=cafEdgedBracketOpen) then begin
  717. ReadTilBracketClose(false);
  718. ExtractNextAtom(phpWithProcModifiers in Attr,Attr);
  719. end else begin
  720. ExtractNextAtom(phpWithProcModifiers in Attr,Attr);
  721. end;
  722. end;
  723. end;
  724. end;
  725. // copy memorystream to Result string
  726. Result:=GetExtraction(phpInUpperCase in Attr);
  727. // add semicolon
  728. if ([phpWithoutSemicolon,phpDoNotAddSemicolon]*Attr=[])
  729. and (Result<>'') and (Result[length(Result)]<>';') then
  730. Result:=Result+';';
  731. end;
  732. function TPascalReaderTool.ExtractProcHeadWithGroup(ProcNode: TCodeTreeNode;
  733. Attr: TProcHeadAttributes): TPascalMethodHeader;
  734. begin
  735. Result.Name := ExtractProcHead(ProcNode, Attr);
  736. Result.Group := ExtractProcedureGroup(ProcNode);
  737. if Result.Group=mgClassOperator then
  738. Result.ResultType := ExtractFuncResultType(ProcNode, Attr);
  739. end;
  740. function TPascalReaderTool.ExtractProcedureHeader(CursorPos: TCodeXYPosition;
  741. Attributes: TProcHeadAttributes; var ProcHead: string): boolean;
  742. var
  743. CleanCursorPos: integer;
  744. ANode: TCodeTreeNode;
  745. begin
  746. Result:=false;
  747. ProcHead:='';
  748. BuildTreeAndGetCleanPos(trTillCursor,lsrEnd,CursorPos,CleanCursorPos,
  749. [btSetIgnoreErrorPos,btCursorPosOutAllowed]);
  750. ANode:=FindDeepestNodeAtPos(CleanCursorPos,True);
  751. while (ANode<>nil) and (ANode.Desc<>ctnProcedure) do
  752. ANode:=ANode.Parent;
  753. if ANode=nil then exit;
  754. ProcHead:=ExtractProcHead(ANode,Attributes);
  755. Result:=true;
  756. end;
  757. function TPascalReaderTool.ExtractClassName(Node: TCodeTreeNode;
  758. InUpperCase: boolean; WithParents: boolean; WithGenericParams: boolean
  759. ): string;
  760. var
  761. ParamsNode: TCodeTreeNode;
  762. ParamNode: TCodeTreeNode;
  763. First: Boolean;
  764. begin
  765. Result:='';
  766. while Node<>nil do begin
  767. case Node.Desc of
  768. ctnTypeDefinition:
  769. begin
  770. if Result<>'' then Result:='.'+Result;
  771. Result:=GetIdentifier(@Src[Node.StartPos])+Result;
  772. if not WithParents then break;
  773. end;
  774. ctnGenericType:
  775. begin
  776. if Result<>'' then Result:='.'+Result;
  777. if (Node.Desc = ctnGenericType) then begin
  778. // extract generic type param names
  779. if WithGenericParams then begin
  780. ParamsNode:=Node.FirstChild.NextBrother;
  781. First:=true;
  782. while ParamsNode<>nil do begin
  783. if ParamsNode.Desc=ctnGenericParams then begin
  784. Result:='>'+Result;
  785. ParamNode:=ParamsNode.FirstChild;
  786. while ParamNode<>nil do begin
  787. if ParamNode.Desc=ctnGenericParameter then begin
  788. if First then
  789. First:=false
  790. else
  791. Result:=','+Result;
  792. Result:=GetIdentifier(@Src[ParamNode.StartPos])+Result;
  793. end;
  794. ParamNode:=ParamNode.NextBrother;
  795. end;
  796. Result:='<'+Result;
  797. end;
  798. ParamsNode:=ParamsNode.NextBrother;
  799. end;
  800. end;
  801. Result:=GetIdentifier(@Src[Node.FirstChild.StartPos])+Result;
  802. end;
  803. if not WithParents then break;
  804. end;
  805. ctnParameterList:
  806. break;
  807. end;
  808. Node:=Node.Parent;
  809. end;
  810. if InUpperCase then
  811. Result:=UpperCaseStr(Result);
  812. end;
  813. function TPascalReaderTool.ExtractClassPath(Node: TCodeTreeNode): string;
  814. var
  815. InArray: Boolean;
  816. begin
  817. Result:='';
  818. InArray:=false;
  819. while Node<>nil do begin
  820. case Node.Desc of
  821. ctnTypeDefinition,ctnGenericType:
  822. begin
  823. if Result<>'' then Result:='.'+Result;
  824. if Node.Desc=ctnTypeDefinition then
  825. Result:=GetIdentifier(@Src[Node.StartPos])+Result
  826. else if Node.FirstChild<>nil then
  827. begin
  828. if (Scanner.CompilerMode = cmDELPHI) and (Node.Desc = ctnGenericType) then
  829. Result := Result + ExtractNode(Node.FirstChild.NextBrother, []);
  830. Result:=GetIdentifier(@Src[Node.FirstChild.StartPos])+Result;
  831. end;
  832. end;
  833. ctnParameterList:
  834. break;
  835. ctnRangedArrayType, ctnOpenArrayType:
  836. begin
  837. InArray := True;
  838. Result := '[]' + Result;
  839. end;
  840. ctnVarDefinition:
  841. if InArray then begin
  842. Result := GetIdentifier(@Src[Node.StartPos]) + Result;
  843. InArray := False;
  844. end;
  845. end;
  846. Node:=Node.Parent;
  847. end;
  848. end;
  849. function TPascalReaderTool.ExtractClassInheritance(
  850. ClassNode: TCodeTreeNode; Attr: TProcHeadAttributes): string;
  851. begin
  852. Result:='';
  853. if (ClassNode=nil) or (not (ClassNode.Desc in AllClasses)) then exit;
  854. MoveCursorToNodeStart(ClassNode);
  855. ReadNextAtom; // class
  856. if UpAtomIs('PACKED') then ReadNextAtom;
  857. if not (UpAtomIs('CLASS') or UpAtomIs('OBJECT') or UpAtomIs('OBJCLASS')
  858. or (UpAtomIs('INTERFACE')))
  859. then
  860. exit;
  861. ReadNextAtom; // '('
  862. if CurPos.Flag<>cafRoundBracketOpen then exit;
  863. ReadNextAtom;
  864. if not AtomIsIdentifier then exit;
  865. MoveCursorToCleanPos(CurPos.StartPos);
  866. ExtractProcHeadPos:=phepNone;
  867. InitExtraction;
  868. while (CurPos.StartPos<=SrcLen) do begin
  869. ExtractNextAtom(true,Attr); // read ancestor/interface
  870. if not AtomIsIdentifier then break;
  871. ExtractNextAtom(true,Attr); // read ','
  872. if not AtomIsChar(',') then break;
  873. end;
  874. // copy memorystream to Result string
  875. Result:=GetExtraction(phpInUpperCase in Attr);
  876. end;
  877. function TPascalReaderTool.ExtractClassNameOfProcNode(ProcNode: TCodeTreeNode;
  878. AddParentClasses: boolean): string;
  879. var
  880. Part: String;
  881. begin
  882. Result:='';
  883. if (ProcNode<>nil) and (ProcNode.Desc=ctnProcedure) then
  884. ProcNode:=ProcNode.FirstChild;
  885. if (ProcNode=nil) or (ProcNode.Desc<>ctnProcedureHead) then exit;
  886. MoveCursorToNodeStart(ProcNode);
  887. repeat
  888. ReadNextAtom;
  889. if not AtomIsIdentifier then break;
  890. Part:=GetAtom;
  891. ReadNextAtom;
  892. if (Scanner.CompilerMode = cmDELPHI) and AtomIsChar('<') then
  893. begin { delphi generics }
  894. Part := Part + GetAtom;
  895. repeat
  896. ReadNextAtom;
  897. Part := Part + GetAtom;
  898. until (CurPos.StartPos > SrcLen) or AtomIsChar('>');
  899. ReadNextAtom;
  900. end;
  901. if (CurPos.Flag<>cafPoint) then break;
  902. if Result<>'' then Result:=Result+'.';
  903. Result:=Result+Part;
  904. until false;
  905. if not AddParentClasses then exit;
  906. Part:=ExtractClassName(ProcNode,false,true);
  907. if Part='' then exit;
  908. Result:=Part+'.'+Result;
  909. end;
  910. function TPascalReaderTool.FindProcNode(StartNode: TCodeTreeNode;
  911. const AProcHead: TPascalMethodHeader; Attr: TProcHeadAttributes;
  912. Visibility: TClassSectionVisibility): TCodeTreeNode;
  913. // search in all next brothers for a Procedure Node with the Name ProcName
  914. // if there are no further brothers and the parent is a section node
  915. // ( e.g. 'interface', 'implementation', ...) or a class visibility node
  916. // (e.g. 'public', 'private', ...) then the search will continue in the next
  917. // section
  918. var
  919. InClass: Boolean;
  920. CurProcHead: TPascalMethodHeader;
  921. begin
  922. Result:=StartNode;
  923. InClass:=FindClassOrInterfaceNode(StartNode)<>nil;
  924. while (Result<>nil) do begin
  925. if Result.Desc=ctnProcedure then begin
  926. if (not ((phpIgnoreForwards in Attr)
  927. and ((Result.SubDesc and ctnsForwardDeclaration)>0)))
  928. and (not ((phpIgnoreProcsWithBody in Attr)
  929. and (FindProcBody(Result)<>nil)))
  930. and (not InClass or IdentNodeIsInVisibleClassSection(Result, Visibility))
  931. then
  932. begin
  933. CurProcHead:=ExtractProcHeadWithGroup(Result,Attr);
  934. //DebugLn(['TPascalReaderTool.FindProcNode B "',CurProcHead,'" =? "',AProcHead,'" Result=',CompareTextIgnoringSpace(CurProcHead,AProcHead,false)]);
  935. if (CurProcHead.Name<>'') and
  936. SameMethodHeaders(AProcHead, CurProcHead)
  937. then
  938. exit;
  939. end;
  940. end;
  941. // next node
  942. if InClass then
  943. Result:=FindNextIdentNodeInClass(Result)
  944. else
  945. Result:=FindNextNodeOnSameLvl(Result);
  946. end;
  947. end;
  948. function TPascalReaderTool.FindProcNode(StartNode: TCodeTreeNode;
  949. const AProcHead: string; AProcSpecType: TPascalMethodGroup;
  950. Attr: TProcHeadAttributes; Visibility: TClassSectionVisibility): TCodeTreeNode;
  951. var
  952. ProcHead: TPascalMethodHeader;
  953. begin
  954. ProcHead.Name := AProcHead;
  955. ProcHead.Group := AProcSpecType;
  956. Result := FindProcNode(StartNode, ProcHead, Attr, Visibility);
  957. end;
  958. function TPascalReaderTool.FindCorrespondingProcNode(ProcNode: TCodeTreeNode;
  959. Attr: TProcHeadAttributes): TCodeTreeNode;
  960. var
  961. ClassNode: TCodeTreeNode;
  962. StartNode: TCodeTreeNode;
  963. ProcHead: TPascalMethodHeader;
  964. begin
  965. Result:=nil;
  966. // get ctnProcedure
  967. //debugln('TPascalReaderTool.FindCorrespondingProcNode Start');
  968. if (ProcNode=nil) then exit;
  969. if ProcNode.Desc=ctnProcedureHead then begin
  970. ProcNode:=ProcNode.Parent;
  971. if (ProcNode=nil) then exit;
  972. end;
  973. if ProcNode.Desc<>ctnProcedure then exit;
  974. // check proc kind
  975. //debugln('TPascalReaderTool.FindCorrespondingProcNode Check kind');
  976. ClassNode:=FindClassOrInterfaceNode(ProcNode);
  977. if ClassNode<>nil then begin
  978. //debugln('TPascalReaderTool.FindCorrespondingProcNode Class');
  979. // in a class definition -> search method body
  980. StartNode:=ClassNode.GetTopMostNodeOfType(ctnTypeSection);
  981. end else if NodeIsMethodBody(ProcNode) then begin
  982. //debugln('TPascalReaderTool.FindCorrespondingProcNode Method ',ExtractClassNameOfProcNode(ProcNode));
  983. // in a method body -> search in class
  984. StartNode:=FindClassNodeInUnit(ExtractClassNameOfProcNode(ProcNode,true),
  985. true,false,false,true);
  986. if StartNode=nil then exit;
  987. if (StartNode<>nil) and (StartNode.Desc in AllClasses)
  988. then begin
  989. StartNode:=StartNode.FirstChild;
  990. while (StartNode<>nil) do begin
  991. if (StartNode.Desc in AllClassBaseSections)
  992. and (StartNode.FirstChild<>nil) then begin
  993. StartNode:=StartNode.FirstChild;
  994. break;
  995. end;
  996. StartNode:=StartNode.NextBrother;
  997. end;
  998. end;
  999. end else begin
  1000. //DebugLn('TPascalReaderTool.FindCorrespondingProcNode Normal');
  1001. // else: search on same lvl
  1002. StartNode:=FindFirstNodeOnSameLvl(ProcNode);
  1003. end;
  1004. if StartNode=nil then exit;
  1005. ProcHead:=ExtractProcHeadWithGroup(ProcNode,Attr);
  1006. //debugln('TPascalReaderTool.FindCorrespondingProcNode StartNode=',StartNode.DescAsString,' ProcHead=',dbgstr(ProcHead),' ',dbgs(Attr),' ',StartNode.DescAsString);
  1007. Result:=FindProcNode(StartNode,ProcHead,Attr);
  1008. if Result=ProcNode then begin
  1009. // found itself -> search further
  1010. StartNode:=FindNextNodeOnSameLvl(Result);
  1011. Result:=FindProcNode(StartNode,ProcHead,Attr);
  1012. end;
  1013. //if Result<>nil then debugln(['TPascalReaderTool.FindCorrespondingProcNode Result=',CleanPosToStr(Result.StartPos),' ',dbgstr(copy(Src,Result.StartPos,50))]);
  1014. end;
  1015. function TPascalReaderTool.FindCorrespondingProcParamNode(
  1016. ProcParamNode: TCodeTreeNode; Attr: TProcHeadAttributes): TCodeTreeNode;
  1017. var
  1018. ProcNode: TCodeTreeNode;
  1019. begin
  1020. Result:=nil;
  1021. if ProcParamNode=nil then exit;
  1022. if (ProcParamNode.Desc=ctnVarDefinition)
  1023. and (ProcParamNode.Parent.Desc=ctnParameterList)
  1024. and (ProcParamNode.Parent.Parent.Desc=ctnProcedureHead) then begin
  1025. // this is a parameter name
  1026. ProcNode:=ProcParamNode.GetNodeOfType(ctnProcedure);
  1027. if ProcNode=nil then exit;
  1028. // search alias for parameter
  1029. ProcNode:=FindCorrespondingProcNode(ProcNode,Attr);
  1030. if ProcNode=nil then exit;
  1031. BuildSubTreeForProcHead(ProcNode);
  1032. Result:=ProcNode;
  1033. while (Result<>nil) do begin
  1034. //debugln(['TPascalReaderTool.FindCorrespondingProcParamNode ',dbgstr(copy(Src,Result.StartPos,20))]);
  1035. if Result.Desc
  1036. in [ctnProcedure,ctnProcedureHead,ctnParameterList]
  1037. then
  1038. Result:=Result.FirstChild
  1039. else begin
  1040. if Result.StartPos<1 then break;
  1041. if CompareIdentifiers(@Src[ProcParamNode.StartPos],@Src[Result.StartPos])=0
  1042. then exit;
  1043. Result:=Result.NextBrother;
  1044. end;
  1045. end;
  1046. Result:=nil;
  1047. end;
  1048. end;
  1049. function TPascalReaderTool.FindDefinitionNameNode(DefinitionNode: TCodeTreeNode
  1050. ): TCodeTreeNode;
  1051. begin
  1052. if DefinitionNode.Desc=ctnGenericType then
  1053. begin
  1054. if DefinitionNode.FirstChild<>nil then
  1055. Result:=DefinitionNode.FirstChild
  1056. else
  1057. Result:=nil;
  1058. end else
  1059. Result:=DefinitionNode;
  1060. end;
  1061. function TPascalReaderTool.FindProcBody(ProcNode: TCodeTreeNode): TCodeTreeNode;
  1062. begin
  1063. Result:=ProcNode;
  1064. if Result=nil then exit;
  1065. if Result.Desc<>ctnProcedure then exit;
  1066. Result:=Result.LastChild;
  1067. while Result<>nil do begin
  1068. if Result.Desc in [ctnBeginBlock,ctnAsmBlock] then
  1069. exit;
  1070. Result:=Result.PriorBrother;
  1071. end;
  1072. end;
  1073. function TPascalReaderTool.ProcBodyIsEmpty(ProcNode: TCodeTreeNode): boolean;
  1074. var
  1075. BodyNode: TCodeTreeNode;
  1076. LastPos: LongInt;
  1077. begin
  1078. Result:=false;
  1079. BodyNode:=FindProcBody(ProcNode);
  1080. if (BodyNode=nil) then exit;
  1081. // check if there are nodes in front (e.g. local variables)
  1082. if (BodyNode.PriorBrother<>nil)
  1083. and (BodyNode.PriorBrother.Desc<>ctnProcedureHead) then
  1084. exit;
  1085. // check if there are child nodes
  1086. if BodyNode.FirstChild<>nil then exit;
  1087. // check if bodynode is only 'asm end' or 'begin end'
  1088. // not even a comment should be there, only spaces are allowed
  1089. if ProcNode.FirstChild.Desc<>ctnProcedureHead then exit;
  1090. MoveCursorToCleanPos(ProcNode.FirstChild.EndPos);
  1091. LastPos:=CurPos.EndPos;
  1092. ReadNextAtom;
  1093. if FindNextNonSpace(Src,LastPos)<>CurPos.StartPos then exit;
  1094. if CurPos.Flag=cafSemicolon then begin
  1095. // semicolon is allowed
  1096. LastPos:=CurPos.EndPos;
  1097. ReadNextAtom;
  1098. if FindNextNonSpace(Src,LastPos)<>CurPos.StartPos then exit;
  1099. end;
  1100. if not (UpAtomIs('ASM') or UpAtomIs('BEGIN')) then exit;
  1101. LastPos:=CurPos.EndPos;
  1102. ReadNextAtom;
  1103. if FindNextNonSpace(Src,LastPos)<>CurPos.StartPos then exit;
  1104. // inherited is allowed
  1105. if UpAtomIs('INHERITED') then begin
  1106. ReadNextAtom;
  1107. if CurPos.Flag=cafSemicolon then begin
  1108. // semicolon is allowed
  1109. LastPos:=CurPos.EndPos;
  1110. ReadNextAtom;
  1111. if FindNextNonSpace(Src,LastPos)<>CurPos.StartPos then exit;
  1112. end;
  1113. end;
  1114. if not UpAtomIs('END') then exit;
  1115. Result:=true;
  1116. end;
  1117. procedure TPascalReaderTool.MoveCursorToFirstProcSpecifier(ProcNode: TCodeTreeNode);
  1118. // After the call,
  1119. // CurPos will stand on the first proc specifier or on a semicolon
  1120. // this can be 'of object'
  1121. begin
  1122. //DebugLn(['TPascalReaderTool.MoveCursorToFirstProcSpecifier ',ProcNode.DescAsString,' ',ProcNode.StartPos]);
  1123. if (ProcNode<>nil) and (ProcNode.Desc in [ctnProcedureType,ctnProcedure]) then
  1124. ProcNode:=ProcNode.FirstChild;
  1125. if (ProcNode=nil) or (ProcNode.Desc<>ctnProcedureHead) then begin
  1126. RaiseException('Internal Error in'
  1127. +' TPascalParserTool.MoveCursorFirstProcSpecifier: '
  1128. +' (ProcNode=nil) or (ProcNode.Desc<>ctnProcedure)');
  1129. end;
  1130. if (ProcNode.LastChild<>nil) and (ProcNode.LastChild.Desc=ctnIdentifier) then
  1131. begin
  1132. // jump behind function result type
  1133. MoveCursorToCleanPos(ProcNode.LastChild.EndPos);
  1134. ReadNextAtom;
  1135. end else if (ProcNode.FirstChild<>nil)
  1136. and (ProcNode.FirstChild.Desc=ctnParameterList)
  1137. then begin
  1138. // jump behind parameter list
  1139. MoveCursorToCleanPos(ProcNode.FirstChild.EndPos);
  1140. ReadNextAtom;
  1141. end else begin
  1142. MoveCursorToNodeStart(ProcNode);
  1143. ReadNextAtom;
  1144. if AtomIsCustomOperator(true,false,false) then begin
  1145. // read name
  1146. ReadNextAtom;
  1147. while (CurPos.Flag=cafPoint) do begin
  1148. ReadNextAtom;
  1149. if CurPos.Flag in [cafPoint,cafRoundBracketOpen,cafEdgedBracketOpen,cafColon,cafEnd,cafSemicolon]
  1150. then break;
  1151. ReadNextAtom;
  1152. end;
  1153. end;
  1154. if (CurPos.Flag=cafRoundBracketOpen) then begin
  1155. // read paramlist
  1156. ReadTilBracketClose(false);
  1157. ReadNextAtom;
  1158. end;
  1159. end;
  1160. if (CurPos.Flag=cafColon) then begin
  1161. // read function result type
  1162. ReadNextAtom;
  1163. if AtomIsIdentifier then begin
  1164. ReadNextAtom;
  1165. while CurPos.Flag=cafPoint do begin
  1166. ReadNextAtom;
  1167. if not AtomIsIdentifier then break;
  1168. ReadNextAtom;
  1169. end;
  1170. end;
  1171. end;
  1172. // CurPos now stands on the first proc specifier or on a semicolon or on the syntax error
  1173. end;
  1174. function TPascalReaderTool.MoveCursorToProcSpecifier(ProcNode: TCodeTreeNode;
  1175. ProcSpec: TProcedureSpecifier): boolean;
  1176. begin
  1177. if ProcNode.FirstChild=nil then begin
  1178. exit(false);
  1179. end;
  1180. MoveCursorToFirstProcSpecifier(ProcNode);
  1181. while (CurPos.StartPos<=ProcNode.FirstChild.EndPos) do begin
  1182. if CurPos.Flag=cafSemicolon then begin
  1183. ReadNextAtom;
  1184. end else begin
  1185. if UpAtomIs(ProcedureSpecifierNames[ProcSpec]) then begin
  1186. Result:=true;
  1187. exit;
  1188. end;
  1189. if (CurPos.Flag=cafEdgedBracketOpen) then begin
  1190. ReadTilBracketClose(false);
  1191. ReadNextAtom;
  1192. end else if UpAtomIs('MESSAGE') then begin
  1193. ReadNextAtom;
  1194. ReadConstant(true,false,[]);
  1195. end else if UpAtomIs('EXTERNAL') then begin
  1196. ReadNextAtom;
  1197. if CurPos.Flag<>cafSemicolon then begin
  1198. if not UpAtomIs('NAME') then
  1199. ReadConstant(true,false,[]);
  1200. if UpAtomIs('NAME') or UpAtomIs('INDEX') then begin
  1201. ReadNextAtom;
  1202. ReadConstant(true,false,[]);
  1203. end;
  1204. end;
  1205. end else begin
  1206. ReadNextAtom;
  1207. end;
  1208. end;
  1209. end;
  1210. Result:=false;
  1211. end;
  1212. procedure TPascalReaderTool.MoveCursorToProcName(ProcNode: TCodeTreeNode;
  1213. SkipClassName: boolean);
  1214. begin
  1215. if (ProcNode.Desc=ctnProcedure) and (ProcNode.FirstChild<>nil)
  1216. and (ProcNode.FirstChild.Desc=ctnProcedureHead) then
  1217. ProcNode:=ProcNode.FirstChild;
  1218. MoveCursorToNodeStart(ProcNode);
  1219. ReadNextAtom;
  1220. if (ProcNode.Desc=ctnProcedure) then begin
  1221. if UpAtomIs('CLASS') then ReadNextAtom;
  1222. ReadNextAtom; // skip proc keyword
  1223. end;
  1224. if not SkipClassName then exit;
  1225. repeat
  1226. ReadNextAtom;
  1227. if CurPos.Flag<>cafPoint then begin
  1228. UndoReadNextAtom;
  1229. break;
  1230. end;
  1231. ReadNextAtom;
  1232. until not AtomIsIdentifier;
  1233. end;
  1234. procedure TPascalReaderTool.MoveCursorBehindProcName(ProcNode: TCodeTreeNode);
  1235. begin
  1236. if (ProcNode.FirstChild<>nil)
  1237. and (ProcNode.FirstChild.Desc=ctnProcedureHead) then
  1238. ProcNode:=ProcNo