PageRenderTime 65ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/components/codetools/pascalreadertool.pas

http://github.com/graemeg/lazarus
Pascal | 3764 lines | 3238 code | 237 blank | 289 comment | 762 complexity | 9588f51c2b0af8cf12bed6b33ebc8a5b MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception
  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:=ProcNode.FirstChild;
  1239. MoveCursorToNodeStart(ProcNode);
  1240. ReadNextAtom;
  1241. if AtomIsIdentifier then begin
  1242. ReadNextAtom;
  1243. while CurPos.Flag=cafPoint do begin
  1244. ReadNextAtom;
  1245. if not AtomIsIdentifier then exit;
  1246. ReadNextAtom;
  1247. end;
  1248. end else if CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen,cafColon]
  1249. then begin
  1250. end else begin
  1251. // operator
  1252. ReadNextAtom;
  1253. end;
  1254. end;
  1255. function TPascalReaderTool.PositionInProcName(ProcNode: TCodeTreeNode;
  1256. SkipClassName: boolean; CleanPos: integer): boolean;
  1257. begin
  1258. if (ProcNode.Desc=ctnProcedure) and (ProcNode.FirstChild<>nil)
  1259. and (ProcNode.FirstChild.Desc=ctnProcedureHead) then
  1260. ProcNode:=ProcNode.FirstChild;
  1261. if (CleanPos<ProcNode.StartPos) or (CleanPos>ProcNode.EndPos) then exit(false);
  1262. MoveCursorToNodeStart(ProcNode);
  1263. ReadNextAtom;
  1264. if (ProcNode.Desc=ctnProcedure) then begin
  1265. if UpAtomIs('CLASS') then ReadNextAtom;
  1266. ReadNextAtom; // skip proc keyword
  1267. end;
  1268. if CleanPos<CurPos.StartPos then exit(false);
  1269. while CurPos.Flag=cafWord do begin
  1270. ReadNextAtom;
  1271. if CurPos.Flag<>cafPoint then begin
  1272. UndoReadNextAtom;
  1273. break;
  1274. end;
  1275. ReadNextAtom;
  1276. end;
  1277. // CurPos is now on the proc name
  1278. if CleanPos>CurPos.EndPos then exit(false);
  1279. if SkipClassName and (CleanPos<CurPos.StartPos) then exit(false);
  1280. Result:=true;
  1281. end;
  1282. function TPascalReaderTool.PositionInFuncResultName(ProcNode: TCodeTreeNode;
  1283. CleanPos: integer): boolean;
  1284. // true if position between ) and :
  1285. var
  1286. Node: TCodeTreeNode;
  1287. begin
  1288. Result:=false;
  1289. if ProcNode=nil then exit;
  1290. if ProcNode.Desc=ctnProcedure then begin
  1291. ProcNode:=ProcNode.FirstChild;
  1292. if ProcNode=nil then exit;
  1293. end;
  1294. if (ProcNode.Desc in [ctnIdentifier,ctnVarDefinition])
  1295. and (ProcNode.Parent<>nil)
  1296. and (ProcNode.Parent.Desc=ctnProcedureHead)
  1297. and (CleanPos>=ProcNode.StartPos) and (CleanPos<=ProcNode.EndPos) then begin
  1298. exit(true);
  1299. end;
  1300. if ProcNode.Desc=ctnProcedureHead then begin
  1301. Node:=ProcNode.FirstChild;
  1302. while (Node<>nil) and (Node.Desc<>ctnIdentifier) do begin
  1303. if (Node.Desc=ctnIdentifier)
  1304. and (CleanPos>=Node.StartPos) and (CleanPos<=Node.EndPos) then
  1305. exit(true);
  1306. Node:=Node.NextBrother;
  1307. end;
  1308. end;
  1309. // read behind parameter list
  1310. if ProcNode.Desc<>ctnProcedureHead then exit;
  1311. if (ProcNode.FirstChild<>nil) and (ProcNode.FirstChild.Desc=ctnParameterList)
  1312. then begin
  1313. if (CleanPos<ProcNode.FirstChild.EndPos) then
  1314. exit;
  1315. MoveCursorToCleanPos(ProcNode.FirstChild.EndPos);
  1316. end else begin
  1317. MoveCursorToNodeStart(ProcNode);
  1318. ReadNextAtom;
  1319. while AtomIsIdentifier do begin
  1320. ReadNextAtom;
  1321. if (CurPos.Flag<>cafPoint) then break;
  1322. ReadNextAtom;
  1323. end;
  1324. if CurPos.Flag=cafRoundBracketOpen then
  1325. if not ReadTilBracketClose(false) then exit;
  1326. end;
  1327. if CurPos.StartPos>CleanPos then exit;
  1328. // read optional result variable (e.g. operator can have them)
  1329. ReadNextAtom;
  1330. if AtomIsIdentifier then ReadNextAtom;
  1331. if CurPos.Flag<>cafColon then exit;
  1332. Result:=CleanPos<=CurPos.StartPos;
  1333. end;
  1334. function TPascalReaderTool.MoveCursorToPropType(PropNode: TCodeTreeNode): boolean;
  1335. begin
  1336. Result:=false;
  1337. if (PropNode=nil)
  1338. or ((PropNode.Desc<>ctnProperty) and (PropNode.Desc<>ctnGlobalProperty)) then
  1339. exit;
  1340. MoveCursorToNodeStart(PropNode);
  1341. ReadNextAtom;
  1342. if (PropNode.Desc=ctnProperty) then begin
  1343. if UpAtomIs('CLASS') then ReadNextAtom;
  1344. if (not UpAtomIs('PROPERTY')) then exit;
  1345. ReadNextAtom;
  1346. end;
  1347. if not AtomIsIdentifier then exit;
  1348. ReadNextAtom;
  1349. if CurPos.Flag=cafEdgedBracketOpen then begin
  1350. ReadTilBracketClose(true);
  1351. ReadNextAtom;
  1352. end;
  1353. if CurPos.Flag in [cafSemicolon,cafEND] then exit;
  1354. if CurPos.Flag<>cafColon then exit;
  1355. ReadNextAtom;
  1356. Result:=CurPos.Flag=cafWord;
  1357. end;
  1358. function TPascalReaderTool.MoveCursorToPropName(PropNode: TCodeTreeNode): boolean;
  1359. begin
  1360. Result:=false;
  1361. if (PropNode=nil)
  1362. or ((PropNode.Desc<>ctnProperty) and (PropNode.Desc<>ctnGlobalProperty)) then
  1363. exit;
  1364. MoveCursorToNodeStart(PropNode);
  1365. ReadNextAtom;
  1366. if (PropNode.Desc=ctnProperty) then begin
  1367. if UpAtomIs('CLASS') then ReadNextAtom;
  1368. if (not UpAtomIs('PROPERTY')) then exit;
  1369. ReadNextAtom;
  1370. end;
  1371. Result:=CurPos.Flag=cafWord;
  1372. end;
  1373. procedure TPascalReaderTool.MoveCursorBehindPropName(PropNode: TCodeTreeNode);
  1374. begin
  1375. if (PropNode=nil)
  1376. or ((PropNode.Desc<>ctnProperty) and (PropNode.Desc<>ctnGlobalProperty)) then
  1377. exit;
  1378. MoveCursorToNodeStart(PropNode);
  1379. ReadNextAtom;
  1380. if (PropNode.Desc=ctnProperty) then begin
  1381. if UpAtomIs('CLASS') then ReadNextAtom;
  1382. if (not UpAtomIs('PROPERTY')) then exit;
  1383. ReadNextAtom;
  1384. end;
  1385. if not AtomIsIdentifier then exit;
  1386. ReadNextAtom;
  1387. end;
  1388. function TPascalReaderTool.ProcNodeHasSpecifier(ProcNode: TCodeTreeNode;
  1389. ProcSpec: TProcedureSpecifier): boolean;
  1390. begin
  1391. Result:=false;
  1392. if ProcNode=nil then exit;
  1393. if ProcNode.Desc=ctnProcedureHead then
  1394. ProcNode:=ProcNode.Parent;
  1395. {$IFDEF CheckNodeTool}
  1396. if ProcNode.Desc<>ctnProcedure then begin
  1397. DebugLn(['TPascalReaderTool.ProcNodeHasSpecifier Desc=',ProcNode.DescAsString]);
  1398. CTDumpStack;
  1399. RaiseException('[TPascalReaderTool.ProcNodeHasSpecifier] '
  1400. +'internal error: invalid ProcNode');
  1401. end;
  1402. {$ENDIF}
  1403. if (ProcNode.FirstChild=nil)
  1404. or ((ProcNode.SubDesc and ctnsNeedJITParsing)>0) then
  1405. BuildSubTreeForProcHead(ProcNode);
  1406. // ToDo: ppu, dcu
  1407. Result:=MoveCursorToProcSpecifier(ProcNode,ProcSpec);
  1408. end;
  1409. function TPascalReaderTool.GetProcNameIdentifier(ProcNode: TCodeTreeNode): PChar;
  1410. begin
  1411. // ToDo: ppu, dcu
  1412. Result:=nil;
  1413. if ProcNode=nil then exit;
  1414. if ProcNode.Desc=ctnProcedure then begin
  1415. ProcNode:=ProcNode.FirstChild;
  1416. if ProcNode=nil then exit;
  1417. end;
  1418. MoveCursorToNodeStart(ProcNode);
  1419. repeat
  1420. ReadNextAtom;
  1421. if not AtomIsIdentifier then exit(nil);
  1422. Result:=@Src[CurPos.StartPos];
  1423. ReadNextAtom;
  1424. until CurPos.Flag<>cafPoint;
  1425. end;
  1426. function TPascalReaderTool.ExtractNode(ANode: TCodeTreeNode;
  1427. Attr: TProcHeadAttributes): string;
  1428. begin
  1429. Result:='';
  1430. ExtractProcHeadPos:=phepNone;
  1431. if (ANode=nil) or (ANode.StartPos<1) then exit;
  1432. InitExtraction;
  1433. // reparse the clean source
  1434. MoveCursorToNodeStart(ANode);
  1435. while (ANode.EndPos>CurPos.StartPos)
  1436. and (CurPos.StartPos<=SrcLen) do
  1437. ExtractNextAtom(true,Attr);
  1438. // copy memorystream to Result string
  1439. Result:=GetExtraction(phpInUpperCase in Attr);
  1440. end;
  1441. function TPascalReaderTool.ExtractCode(StartPos, EndPos: integer;
  1442. Attr: TProcHeadAttributes): string;
  1443. begin
  1444. Result:='';
  1445. ExtractProcHeadPos:=phepNone;
  1446. if (StartPos<1) or (StartPos>=EndPos) or (StartPos>SrcLen) then exit;
  1447. InitExtraction;
  1448. // reparse the clean source
  1449. MoveCursorToCleanPos(StartPos);
  1450. while (EndPos>CurPos.StartPos)
  1451. and (CurPos.StartPos<=SrcLen) do
  1452. ExtractNextAtom(true,Attr);
  1453. // copy memorystream to Result string
  1454. Result:=GetExtraction(phpInUpperCase in Attr);
  1455. end;
  1456. function TPascalReaderTool.ExtractBrackets(BracketStartPos: integer;
  1457. Attr: TProcHeadAttributes): string;
  1458. function ExtractTilBracketClose(ExtractBrackets: boolean): boolean;
  1459. var
  1460. CloseBracket: TCommonAtomFlag;
  1461. First: Boolean;
  1462. begin
  1463. Result:=true;
  1464. case CurPos.Flag of
  1465. cafRoundBracketOpen: CloseBracket:=cafRoundBracketClose;
  1466. cafEdgedBracketOpen: CloseBracket:=cafEdgedBracketClose;
  1467. else exit;
  1468. end;
  1469. First:=true;
  1470. repeat
  1471. if First then
  1472. ExtractNextAtom(ExtractBrackets,Attr)
  1473. else
  1474. ExtractNextAtom(true,Attr);
  1475. if CurPos.StartPos>SrcLen then exit;
  1476. if CurPos.Flag=CloseBracket then exit(true);
  1477. if CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen] then begin
  1478. if not ExtractTilBracketClose(true) then exit;
  1479. end;
  1480. until false;
  1481. end;
  1482. begin
  1483. Result:='';
  1484. ExtractProcHeadPos:=phepNone;
  1485. if (BracketStartPos<1) or (BracketStartPos>SrcLen) then exit;
  1486. InitExtraction;
  1487. // reparse the clean source
  1488. MoveCursorToCleanPos(BracketStartPos);
  1489. ReadNextAtom;
  1490. if not ExtractTilBracketClose(not (phpWithoutBrackets in Attr)) then exit;
  1491. if not (phpWithoutBrackets in Attr) then
  1492. ExtractNextAtom(true,Attr);
  1493. // copy memorystream to Result string
  1494. Result:=GetExtraction(phpInUpperCase in Attr);
  1495. end;
  1496. function TPascalReaderTool.ExtractIdentifierWithPoints(StartPos: integer;
  1497. ExceptionOnError: boolean): string;
  1498. begin
  1499. Result:='';
  1500. MoveCursorToCleanPos(StartPos);
  1501. ReadNextAtom;
  1502. if not AtomIsIdentifierE(ExceptionOnError) then exit;
  1503. Result:=GetAtom;
  1504. repeat
  1505. ReadNextAtom;
  1506. if CurPos.Flag<>cafPoint then
  1507. exit;
  1508. ReadNextAtom;
  1509. if not AtomIsIdentifierE(ExceptionOnError) then exit;
  1510. Result+='.'+GetAtom;
  1511. until false;
  1512. end;
  1513. function TPascalReaderTool.ExtractPropName(PropNode: TCodeTreeNode;
  1514. InUpperCase: boolean): string;
  1515. begin
  1516. Result:='';
  1517. if not MoveCursorToPropName(PropNode) then exit;
  1518. if InUpperCase then
  1519. Result:=GetUpAtom
  1520. else
  1521. Result:=GetAtom;
  1522. end;
  1523. function TPascalReaderTool.ExtractProperty(PropNode: TCodeTreeNode;
  1524. Attr: TProcHeadAttributes): string;
  1525. begin
  1526. Result:='';
  1527. ExtractProcHeadPos:=phepNone;
  1528. if (PropNode=nil) or (PropNode.StartPos<1)
  1529. or ((PropNode.Desc<>ctnProperty) and (PropNode.Desc<>ctnGlobalProperty)) then
  1530. exit;
  1531. // start extraction
  1532. InitExtraction;
  1533. MoveCursorToNodeStart(PropNode);
  1534. ExtractNextAtom(false,Attr);
  1535. if (PropNode.Desc=ctnProperty) then begin
  1536. if UpAtomIs('CLASS') then
  1537. ExtractNextAtom(phpWithStart in Attr,Attr);
  1538. // parse 'property'
  1539. ExtractNextAtom(phpWithStart in Attr,Attr);
  1540. end;
  1541. ExtractProcHeadPos:=phepStart;
  1542. // parse name
  1543. ExtractNextAtom(not (phpWithoutName in Attr),Attr);
  1544. ExtractProcHeadPos:=phepName;
  1545. // read parameter list
  1546. if (CurPos.Flag=cafEdgedBracketOpen) then
  1547. ReadParamList(false,true,Attr);
  1548. ExtractProcHeadPos:=phepParamList;
  1549. // read result type
  1550. if (CurPos.Flag=cafColon) then begin
  1551. ExtractNextAtom(phpWithResultType in Attr,Attr);
  1552. if not AtomIsIdentifier then exit;
  1553. ExtractNextAtom(phpWithResultType in Attr,Attr);
  1554. if CurPos.Flag=cafPoint then begin
  1555. // unit.type
  1556. ExtractNextAtom(phpWithResultType in Attr,Attr);
  1557. if not AtomIsIdentifier then exit;
  1558. ExtractNextAtom(phpWithResultType in Attr,Attr);
  1559. end;
  1560. ExtractProcHeadPos:=phepResultType;
  1561. end;
  1562. // copy memorystream to Result string
  1563. Result:=GetExtraction(phpInUpperCase in Attr);
  1564. end;
  1565. function TPascalReaderTool.GetPropertyNameIdentifier(PropNode: TCodeTreeNode): PChar;
  1566. begin
  1567. // ToDo: ppu, dcu
  1568. Result:=nil;
  1569. if PropNode=nil then exit;
  1570. if not MoveCursorToPropName(PropNode) then exit;
  1571. Result:=@Src[CurPos.StartPos];
  1572. end;
  1573. function TPascalReaderTool.GetPropertyTypeIdentifier(PropNode: TCodeTreeNode): PChar;
  1574. begin
  1575. // ToDo: ppu, dcu
  1576. Result:=nil;
  1577. if PropNode=nil then exit;
  1578. if not MoveCursorToPropType(PropNode) then exit;
  1579. Result:=@Src[CurPos.StartPos];
  1580. end;
  1581. function TPascalReaderTool.PositionInPropertyName(PropNode: TCodeTreeNode;
  1582. CleanPos: integer): boolean;
  1583. begin
  1584. if PropNode=nil then exit(false);
  1585. MoveCursorToNodeStart(PropNode);
  1586. if (PropNode.Desc=ctnProperty) then begin
  1587. ReadNextAtom; // read 'property'
  1588. if UpAtomIs('CLASS') then ReadNextAtom;
  1589. end;
  1590. ReadNextAtom; // read name
  1591. Result:=(CurPos.Flag=cafWord)
  1592. and (CleanPos>=CurPos.StartPos) and (CleanPos<=CurPos.EndPos);
  1593. end;
  1594. function TPascalReaderTool.ExtractIdentCharsFromStringConstant(StartPos,
  1595. MinPos, MaxPos, MaxLen: integer): string;
  1596. var
  1597. APos: Integer;
  1598. IdentStartPos: Integer;
  1599. IdentStr: String;
  1600. IdentEndPos: LongInt;
  1601. begin
  1602. Result:='';
  1603. APos:=StartPos;
  1604. while APos<SrcLen do begin
  1605. if (Src[APos]='#') then begin
  1606. // skip char constant
  1607. inc(APos);
  1608. if IsNumberChar[Src[APos]] then begin
  1609. while (APos<CurPos.EndPos) and IsNumberChar[Src[APos]] do
  1610. inc(APos)
  1611. end else if Src[APos]='$' then begin
  1612. while (APos<CurPos.EndPos) and IsHexNumberChar[Src[APos]] do
  1613. inc(APos);
  1614. end;
  1615. end else if (Src[APos]='''') then begin
  1616. inc(APos);
  1617. repeat
  1618. // read identifier chars
  1619. IdentStartPos:=APos;
  1620. while (APos<SrcLen) and (IsIdentChar[Src[APos]]) do
  1621. inc(APos);
  1622. IdentEndPos:=APos;
  1623. if IdentStartPos<MinPos then IdentStartPos:=MinPos;
  1624. if IdentEndPos>MaxPos then IdentEndPos:=MaxPos;
  1625. if (IdentEndPos>IdentStartPos) then begin
  1626. if IdentEndPos-IdentStartPos+length(Result)>MaxLen then
  1627. IdentEndPos:=IdentStartPos+MaxLen-length(Result);
  1628. IdentStr:=copy(Src,IdentStartPos,IdentEndPos-IdentStartPos);
  1629. if (IdentStr<>'') then begin
  1630. IdentStr[1]:=UpChars[IdentStr[1]];
  1631. Result:=Result+IdentStr;
  1632. end;
  1633. end;
  1634. // skip non identifier chars
  1635. while (APos<SrcLen) and (Src[APos]<>'''')
  1636. and (not IsIdentChar[Src[APos]])
  1637. do
  1638. inc(APos);
  1639. until (APos>=SrcLen) or (Src[APos]='''') or (length(Result)>=MaxLen);
  1640. inc(APos);
  1641. end else
  1642. break;
  1643. end;
  1644. end;
  1645. function TPascalReaderTool.ReadStringConstantValue(StartPos: integer): string;
  1646. // reads a string constant and returns the resulting string
  1647. var
  1648. Run: Integer;
  1649. NumberStart: PChar;
  1650. ResultLen: Integer;
  1651. Number: Integer;
  1652. p: PChar;
  1653. begin
  1654. Result:='';
  1655. if StartPos>SrcLen then exit;
  1656. // first read and calculate the resulting length, then copy the chars
  1657. for Run:=1 to 2 do begin
  1658. ResultLen:=0;
  1659. p:=@Src[StartPos];
  1660. while true do begin
  1661. case p^ of
  1662. '''':
  1663. begin
  1664. // read string
  1665. inc(p);
  1666. while true do begin
  1667. if p^='''' then begin
  1668. if p[1]='''' then begin
  1669. // a double ' means a single '
  1670. inc(ResultLen);
  1671. if Run=2 then Result[ResultLen]:='''';
  1672. inc(p,2);
  1673. end else begin
  1674. // a single ' means end of string constant
  1675. inc(p);
  1676. break;
  1677. end;
  1678. end else begin
  1679. // normal char
  1680. inc(ResultLen);
  1681. if Run=2 then Result[ResultLen]:=p^;
  1682. inc(p);
  1683. end;
  1684. end;
  1685. end;
  1686. '#':
  1687. begin
  1688. // read char constant
  1689. inc(p);
  1690. NumberStart:=p;
  1691. if IsNumberChar[p^] then begin
  1692. // read decimal number
  1693. while IsNumberChar[p^] do
  1694. inc(p);
  1695. Number:=StrToIntDef(copy(Src,NumberStart-PChar(Src)+1,p-NumberStart),-1);
  1696. end else if p^='$' then begin
  1697. // read hexnumber
  1698. inc(p);
  1699. while IsHexNumberChar[p^] do
  1700. inc(p);
  1701. Number:=HexStrToIntDef(NumberStart,-1);
  1702. end else
  1703. Number:=-1;
  1704. // add special character
  1705. if (Number<0) or (Number>255) then break;
  1706. inc(ResultLen);
  1707. if Run=2 then Result[ResultLen]:=chr(Number);
  1708. end;
  1709. '^':
  1710. begin
  1711. inc(p);
  1712. if p^ in ['A'..'Z'] then begin
  1713. inc(ResultLen);
  1714. if Run=2 then Result[ResultLen]:=chr(ord(p^)-ord('A'));
  1715. end else begin
  1716. break;
  1717. end;
  1718. end;
  1719. else
  1720. break;
  1721. end;
  1722. end;
  1723. if Run=1 then SetLength(Result,ResultLen);
  1724. end;
  1725. end;
  1726. function TPascalReaderTool.GetNodeIdentifier(Node: TCodeTreeNode): PChar;
  1727. begin
  1728. Result:=nil;
  1729. if (Node=nil) or (Node.StartPos>SrcLen) then exit;
  1730. case Node.Desc of
  1731. ctnProcedure,ctnProcedureHead:
  1732. Result:=GetProcNameIdentifier(Node);
  1733. ctnProperty:
  1734. Result:=GetPropertyNameIdentifier(Node);
  1735. ctnTypeDefinition,ctnVarDefinition,ctnConstDefinition,
  1736. ctnEnumIdentifier,ctnIdentifier:
  1737. Result:=@Src[Node.StartPos];
  1738. end;
  1739. end;
  1740. function TPascalReaderTool.GetHintModifiers(Node: TCodeTreeNode): TPascalHintModifiers;
  1741. function IsHintModifier: boolean;
  1742. begin
  1743. if CurPos.Flag<>cafWord then exit(false);
  1744. Result:=true;
  1745. if UpAtomIs('PLATFORM') then
  1746. Include(GetHintModifiers,phmPlatform)
  1747. else if UpAtomIs('UNIMPLEMENTED') then
  1748. Include(GetHintModifiers,phmUnimplemented)
  1749. else if UpAtomIs('LIBRARY') then
  1750. Include(GetHintModifiers,phmLibrary)
  1751. else if UpAtomIs('EXPERIMENTAL') then
  1752. Include(GetHintModifiers,phmExperimental)
  1753. else if UpAtomIs('DEPRECATED') then
  1754. Include(GetHintModifiers,phmDeprecated)
  1755. else
  1756. Result:=false;
  1757. end;
  1758. begin
  1759. Result:=[];
  1760. if Node=nil then exit;
  1761. case Node.Desc of
  1762. ctnProgram,ctnPackage,ctnLibrary,ctnUnit:
  1763. begin
  1764. MoveCursorToNodeStart(Node);
  1765. ReadNextAtom;
  1766. if not (UpAtomIs('PROGRAM') or UpAtomIs('PACKAGE') or UpAtomIs('LIBRARY')
  1767. or UpAtomIs('UNIT')) then exit;
  1768. ReadNextAtom;// name
  1769. while IsHintModifier do ReadNextAtom;
  1770. end;
  1771. ctnProcedure,ctnProcedureType,ctnProcedureHead:
  1772. begin
  1773. if Node.Desc<>ctnProcedureHead then begin
  1774. Node:=Node.FirstChild;
  1775. if Node=nil then exit;
  1776. end;
  1777. MoveCursorToFirstProcSpecifier(Node);
  1778. // ToDo:
  1779. end;
  1780. ctnProperty:
  1781. begin
  1782. Node:=Node.LastChild;
  1783. while Node<>nil do begin
  1784. if Node.Desc=ctnHintModifier then begin
  1785. MoveCursorToNodeStart(Node);
  1786. ReadNextAtom;
  1787. IsHintModifier;
  1788. end;
  1789. Node:=Node.PriorBrother;
  1790. end;
  1791. end;
  1792. ctnVarDefinition,ctnConstant,ctnConstDefinition,
  1793. ctnTypeDefinition,ctnGenericType:
  1794. begin
  1795. Node:=FindTypeNodeOfDefinition(Node);
  1796. if Node=nil then exit;
  1797. while (Node<>nil) do begin
  1798. if Node.Desc=ctnHintModifier then begin
  1799. MoveCursorToNodeStart(Node);
  1800. ReadNextAtom;
  1801. IsHintModifier;
  1802. end;
  1803. Node:=Node.NextBrother;
  1804. end;
  1805. end;
  1806. end;
  1807. end;
  1808. procedure TPascalReaderTool.ForEachIdentifierInCleanSrc(StartPos,
  1809. EndPos: integer; SkipComments: boolean; Node: TCodeTreeNode;
  1810. const OnIdentifier: TOnEachPRIdentifier; Data: pointer; var Abort: boolean);
  1811. var
  1812. CommentLvl: Integer;
  1813. InStrConst: Boolean;
  1814. p: PChar;
  1815. EndP: Pointer;
  1816. Range: TEPRIRange;
  1817. procedure SkipIdentifier; inline;
  1818. begin
  1819. while (p<EndP) and IsIdentChar[p^] do inc(p);
  1820. end;
  1821. begin
  1822. //debugln(['TPascalReaderTool.ForEachIdentifierInCleanSrc Node=',Node.DescAsString,' "',dbgstr(Src,StartPos,EndPos-StartPos),'"']);
  1823. if (StartPos<1) then
  1824. StartPos:=1;
  1825. if StartPos>SrcLen then exit;
  1826. if EndPos>SrcLen then EndPos:=SrcLen+1;
  1827. if StartPos>=EndPos then exit;
  1828. Range:=epriInCode;
  1829. p:=@Src[StartPos];
  1830. EndP:=p+EndPos-StartPos;
  1831. while p<EndP do begin
  1832. case p^ of
  1833. '{':
  1834. begin
  1835. inc(p);
  1836. if p^=#3 then begin
  1837. // codetools skip comment {#3 #3}
  1838. inc(p);
  1839. repeat
  1840. if p>=EndP then exit;
  1841. if (p^=#3) and (p[1]='}')
  1842. then begin
  1843. inc(p,2);
  1844. break;
  1845. end;
  1846. inc(p);
  1847. until false;
  1848. end else begin
  1849. // pascal comment {}
  1850. CommentLvl:=1;
  1851. InStrConst:=false;
  1852. if p^='$' then
  1853. Range:=epriInDirective
  1854. else
  1855. Range:=epriInComment;
  1856. repeat
  1857. if p>=EndP then exit;
  1858. case p^ of
  1859. '{': if Scanner.NestedComments then inc(CommentLvl);
  1860. '}':
  1861. begin
  1862. dec(CommentLvl);
  1863. if CommentLvl=0 then break;
  1864. end;
  1865. 'a'..'z','A'..'Z','_':
  1866. if not InStrConst then begin
  1867. if not SkipComments then begin
  1868. OnIdentifier(Self,p-PChar(Src)+1,Range,Node,Data,Abort);
  1869. SkipIdentifier;
  1870. if Abort then exit;
  1871. end;
  1872. while (p<EndP) and IsIdentChar[p^] do inc(p);
  1873. end;
  1874. '''':
  1875. InStrConst:=not InStrConst;
  1876. #10,#13:
  1877. InStrConst:=false;
  1878. end;
  1879. inc(p);
  1880. until false;
  1881. inc(p);
  1882. //debugln(StartPos,' ',copy(Src,CommentStart,StartPos-CommentStart));
  1883. end;
  1884. end;
  1885. '/': // Delphi comment
  1886. if p[1]<>'/' then begin
  1887. inc(p);
  1888. end else begin
  1889. inc(p,2);
  1890. InStrConst:=false;
  1891. repeat
  1892. if p>=EndP then exit;
  1893. case p^ of
  1894. #10,#13:
  1895. break;
  1896. 'a'..'z','A'..'Z','_':
  1897. if not InStrConst then begin
  1898. if not SkipComments then begin
  1899. OnIdentifier(Self,p-PChar(Src)+1,Range,Node,Data,Abort);
  1900. SkipIdentifier;
  1901. if Abort then exit;
  1902. end;
  1903. while (p<EndP) and IsIdentChar[p^] do inc(p);
  1904. end;
  1905. '''':
  1906. InStrConst:=not InStrConst;
  1907. end;
  1908. inc(p);
  1909. until false;
  1910. inc(p);
  1911. if (p<EndP) and (p^ in [#10,#13])
  1912. and (p[-1]<>p^) then
  1913. inc(p);
  1914. end;
  1915. '(': // turbo pascal comment
  1916. if (p[1]<>'*') then begin
  1917. inc(p);
  1918. end else begin
  1919. inc(p,3);
  1920. InStrConst:=false;
  1921. repeat
  1922. if p>=EndP then exit;
  1923. case p^ of
  1924. ')':
  1925. if p[-1]='*' then break;
  1926. 'a'..'z','A'..'Z','_':
  1927. if not InStrConst then begin
  1928. if not SkipComments then begin
  1929. OnIdentifier(Self,p-PChar(Src)+1,Range,Node,Data,Abort);
  1930. SkipIdentifier;
  1931. if Abort then exit;
  1932. end;
  1933. SkipIdentifier;
  1934. end;
  1935. '''':
  1936. InStrConst:=not InStrConst;
  1937. #10,#13:
  1938. InStrConst:=false;
  1939. end;
  1940. inc(p);
  1941. until false;
  1942. inc(p);
  1943. end;
  1944. 'a'..'z','A'..'Z','_':
  1945. begin
  1946. OnIdentifier(Self,p-PChar(Src)+1,epriInCode,Node,Data,Abort);
  1947. SkipIdentifier;
  1948. if Abort then exit;
  1949. end;
  1950. '''':
  1951. begin
  1952. // skip string constant
  1953. inc(p);
  1954. while p<EndP do begin
  1955. if (not (p^ in ['''',#10,#13])) then
  1956. inc(p)
  1957. else begin
  1958. inc(p);
  1959. break;
  1960. end;
  1961. end;
  1962. end;
  1963. else
  1964. inc(p);
  1965. end;
  1966. end;
  1967. end;
  1968. procedure TPascalReaderTool.ForEachIdentifierInNode(Node: TCodeTreeNode;
  1969. SkipComments: boolean; const OnIdentifier: TOnEachPRIdentifier;
  1970. Data: Pointer; var Abort: boolean);
  1971. var
  1972. StartPos: Integer;
  1973. EndPos: Integer;
  1974. Child: TCodeTreeNode;
  1975. begin
  1976. //debugln(['TPascalReaderTool.ForEachIdentifierInNode START ',Node.DescAsString]);
  1977. if NodeNeedsBuildSubTree(Node) then
  1978. BuildSubTree(Node);
  1979. if Node.FirstChild<>nil then begin
  1980. EndPos:=Node.StartPos;
  1981. Child:=Node.FirstChild;
  1982. while Child<>nil do begin
  1983. // scan in front of child
  1984. ForEachIdentifierInCleanSrc(EndPos,Child.StartPos,SkipComments,
  1985. Node,OnIdentifier,Data,Abort);
  1986. if Abort then exit;
  1987. // scan child
  1988. ForEachIdentifierInNode(Child,SkipComments,OnIdentifier,Data,Abort);
  1989. if Abort then exit;
  1990. EndPos:=Child.EndPos;
  1991. Child:=Child.NextBrother;
  1992. end;
  1993. // scan behind children
  1994. ForEachIdentifierInCleanSrc(EndPos,Node.EndPos,SkipComments,
  1995. Node,OnIdentifier,Data,Abort);
  1996. end else begin
  1997. // leaf node
  1998. StartPos:=Node.StartPos;
  1999. EndPos:=Node.EndPos;
  2000. // nodes without children can overlap with their NextBrother
  2001. if (Node.NextBrother<>nil)
  2002. and (Node.NextBrother.StartPos<EndPos) then
  2003. EndPos:=Node.NextBrother.StartPos;
  2004. // scan node range
  2005. ForEachIdentifierInCleanSrc(StartPos,EndPos,SkipComments,
  2006. Node,OnIdentifier,Data,Abort);
  2007. end;
  2008. end;
  2009. procedure TPascalReaderTool.ForEachIdentifier(SkipComments: boolean;
  2010. const OnIdentifier: TOnEachPRIdentifier; Data: Pointer);
  2011. var
  2012. Node: TCodeTreeNode;
  2013. Abort: boolean;
  2014. begin
  2015. //debugln(['TPascalReaderTool.ForEachIdentifier START']);
  2016. Node:=Tree.Root;
  2017. Abort:=false;
  2018. while Node<>nil do begin
  2019. ForEachIdentifierInNode(Node,SkipComments,OnIdentifier,Data,Abort);
  2020. if Abort then exit;
  2021. Node:=Node.NextBrother;
  2022. end;
  2023. end;
  2024. function TPascalReaderTool.FindVarNode(StartNode: TCodeTreeNode;
  2025. const UpperVarName: string; Visibility: TClassSectionVisibility
  2026. ): TCodeTreeNode;
  2027. var
  2028. InClass: Boolean;
  2029. begin
  2030. Result:=StartNode;
  2031. InClass:=FindClassOrInterfaceNode(StartNode)<>nil;
  2032. while Result<>nil do begin
  2033. if (Result.Desc=ctnVarDefinition)
  2034. and (not InClass or IdentNodeIsInVisibleClassSection(Result, Visibility))
  2035. and (CompareNodeIdentChars(Result,UpperVarName)=0) then
  2036. exit;
  2037. if InClass then
  2038. Result:=FindNextIdentNodeInClass(Result)
  2039. else
  2040. Result:=FindNextNodeOnSameLvl(Result);
  2041. end;
  2042. end;
  2043. function TPascalReaderTool.FindTypeNodeOfDefinition(
  2044. DefinitionNode: TCodeTreeNode): TCodeTreeNode;
  2045. // for example: 'var a,b,c: integer;' only c has a type child
  2046. begin
  2047. Result:=DefinitionNode;
  2048. while (Result<>nil)
  2049. and (Result.Desc in AllIdentifierDefinitions) do begin
  2050. if (Result.FirstChild<>nil) then begin
  2051. Result:=Result.FirstChild;
  2052. if Result.Desc=ctnGenericName then begin
  2053. // skip generic name and params
  2054. Result:=Result.NextBrother;
  2055. if Result=nil then exit;
  2056. Result:=Result.NextBrother;
  2057. if Result=nil then exit;
  2058. end;
  2059. if (not (Result.Desc in AllPascalTypes)) then
  2060. Result:=nil;
  2061. exit;
  2062. end;
  2063. if Result.Desc<>ctnVarDefinition then exit(nil);
  2064. Result:=Result.NextBrother;
  2065. end;
  2066. end;
  2067. function TPascalReaderTool.FindClassNode(StartNode: TCodeTreeNode;
  2068. const AClassName: string; IgnoreForwards, IgnoreNonForwards: boolean): TCodeTreeNode;
  2069. // search for class like types on same level
  2070. var
  2071. ANode, CurClassNode: TCodeTreeNode;
  2072. NameNode: TCodeTreeNode;
  2073. p: PChar;
  2074. begin
  2075. ANode:=StartNode;
  2076. Result:=nil;
  2077. if AClassName='' then exit;
  2078. p:=PChar(AClassName);
  2079. while (ANode<>nil) do begin
  2080. if ANode.Desc in [ctnTypeDefinition,ctnGenericType] then begin
  2081. //debugln(['TPascalReaderTool.FindClassNode ',GetIdentifier(@Src[ANode.StartPos])]);
  2082. CurClassNode:=FindTypeNodeOfDefinition(ANode);
  2083. if (CurClassNode<>nil)
  2084. and (CurClassNode.Desc in AllClassObjects) then begin
  2085. if (not (IgnoreForwards
  2086. and ((CurClassNode.SubDesc and ctnsForwardDeclaration)>0)))
  2087. and (not (IgnoreNonForwards
  2088. and ((CurClassNode.SubDesc and ctnsForwardDeclaration)=0)))
  2089. then begin
  2090. NameNode:=ANode;
  2091. if (ANode.Desc=ctnGenericType) and (ANode.FirstChild<>nil) then
  2092. NameNode:=ANode.FirstChild;
  2093. //debugln(['TPascalReaderTool.FindClassNode class name = "',GetIdentifier(@Src[NameNode.StartPos]),'"']);
  2094. if NameNode.StartPos>SrcLen then exit;
  2095. if CompareIdentifiers(p,@Src[NameNode.StartPos])=0 then begin
  2096. Result:=FindNestedClass(CurClassNode,p,true);
  2097. exit;
  2098. end;
  2099. end;
  2100. end;
  2101. end;
  2102. // next node
  2103. if (ANode.Desc in [ctnTypeSection]+AllCodeSections)
  2104. and (ANode.FirstChild<>nil) then
  2105. ANode:=ANode.FirstChild
  2106. else if ANode.NextBrother<>nil then
  2107. ANode:=ANode.NextBrother
  2108. else begin
  2109. // skip procs, const and var sections
  2110. repeat
  2111. ANode:=ANode.Parent;
  2112. if (ANode=nil) then exit;
  2113. if (not (ANode.Desc in [ctnTypeSection]+AllCodeSections)) then exit;
  2114. if ANode.NextBrother<>nil then begin
  2115. ANode:=ANode.NextBrother;
  2116. break;
  2117. end;
  2118. until false;
  2119. end;
  2120. end;
  2121. end;
  2122. function TPascalReaderTool.FindClassNodeBackwards(StartNode: TCodeTreeNode;
  2123. const AClassName: string; IgnoreForwards, IgnoreNonForwards: boolean): TCodeTreeNode;
  2124. var
  2125. ANode: TCodeTreeNode;
  2126. CurClassNode: TCodeTreeNode;
  2127. p: PChar;
  2128. begin
  2129. ANode:=StartNode;
  2130. p:=PChar(AClassName);
  2131. while ANode<>nil do begin
  2132. if ANode.Desc=ctnTypeDefinition then begin
  2133. CurClassNode:=ANode.FirstChild;
  2134. if (CurClassNode<>nil)
  2135. and (CurClassNode.Desc in AllClassObjects) then begin
  2136. if (not (IgnoreForwards
  2137. and ((CurClassNode.SubDesc and ctnsForwardDeclaration)>0)))
  2138. and (not (IgnoreNonForwards
  2139. and ((CurClassNode.SubDesc and ctnsForwardDeclaration)=0)))
  2140. then begin
  2141. if CompareIdentifiers(p,@Src[ANode.StartPos])=0 then begin
  2142. Result:=FindNestedClass(CurClassNode,p,true);
  2143. exit;
  2144. end;
  2145. end;
  2146. end;
  2147. end;
  2148. if ANode.PriorBrother<>nil then begin
  2149. ANode:=ANode.PriorBrother;
  2150. if (ANode.FirstChild<>nil) and (ANode.Desc in AllCodeSections) then
  2151. ANode:=ANode.LastChild;
  2152. if (ANode.FirstChild<>nil) and (ANode.Desc in AllDefinitionSections) then
  2153. ANode:=ANode.LastChild;
  2154. end else begin
  2155. ANode:=ANode.Parent;
  2156. end;
  2157. end;
  2158. Result:=nil;
  2159. end;
  2160. function TPascalReaderTool.FindNestedClass(RootClassNode: TCodeTreeNode;
  2161. AClassName: PChar; SkipFirst: boolean): TCodeTreeNode;
  2162. var
  2163. p: PChar;
  2164. Node: TCodeTreeNode;
  2165. EndNode: TCodeTreeNode;
  2166. begin
  2167. Result:=nil;
  2168. if RootClassNode=nil then exit;
  2169. if AClassName=nil then exit;
  2170. p:=AClassName;
  2171. if SkipFirst then begin
  2172. while IsIdentChar[p^] do inc(p);
  2173. if p^='<' then
  2174. begin
  2175. while not (p^ in [#0,'>']) do Inc(p);
  2176. if p^ = '>' then Inc(p);
  2177. end;
  2178. if p^=#0 then exit(RootClassNode);
  2179. if p^<>'.' then exit;
  2180. inc(p);
  2181. end;
  2182. //debugln(['TPascalReaderTool.FindNestedClass p="',p,'"']);
  2183. if not IsIdentStartChar[p^] then exit;
  2184. EndNode:=RootClassNode.NextSkipChilds;
  2185. Node:=RootClassNode.Next;
  2186. while Node<>EndNode do begin
  2187. // debugln(['TPascalReaderTool.FindNestedClass Node=',node.DescAsString]);
  2188. if Node.Desc in [ctnTypeDefinition,ctnGenericType] then begin
  2189. if (Node.LastChild<>nil) and (Node.LastChild.Desc in AllClasses) then begin
  2190. if ((Node.Desc=ctnTypeDefinition)
  2191. and (CompareIdentifierPtrs(p,@Src[Node.StartPos])=0))
  2192. or ((Node.FirstChild.Desc=ctnGenericName)
  2193. and (CompareIdentifierPtrs(p,@Src[Node.FirstChild.StartPos])=0))
  2194. then begin
  2195. // class found
  2196. Node:=Node.LastChild;
  2197. while IsIdentChar[p^] do inc(p);
  2198. if p^=#0 then exit(Node);
  2199. if p^<>'.' then exit;
  2200. Result:=FindNestedClass(Node,p+1,false);
  2201. exit;
  2202. end;
  2203. end;
  2204. end;
  2205. if Node.Desc in AllClassSections then
  2206. Node:=Node.Next
  2207. else
  2208. Node:=Node.NextSkipChilds;
  2209. end;
  2210. end;
  2211. function TPascalReaderTool.FindClassNode(CursorNode: TCodeTreeNode): TCodeTreeNode;
  2212. // find class node of a node in a procedure (declaration or body)
  2213. begin
  2214. while CursorNode<>nil do begin
  2215. if CursorNode.Desc in AllClassObjects then begin
  2216. Result:=CursorNode;
  2217. exit;
  2218. end else if NodeIsMethodBody(CursorNode) then begin
  2219. Result:=FindClassNodeForMethodBody(CursorNode,true,false);
  2220. exit;
  2221. end;
  2222. CursorNode:=CursorNode.Parent;
  2223. end;
  2224. Result:=nil;
  2225. end;
  2226. function TPascalReaderTool.FindClassNodeForMethodBody(ProcNode: TCodeTreeNode;
  2227. IgnoreForwards, IgnoreNonForwards: boolean): TCodeTreeNode;
  2228. var
  2229. ProcClassName: String;
  2230. begin
  2231. Result:=nil;
  2232. ProcClassName:=ExtractClassNameOfProcNode(ProcNode,true);
  2233. if ProcClassName='' then exit;
  2234. Result:=FindClassNodeBackwards(ProcNode,ProcClassName,IgnoreForwards,
  2235. IgnoreNonForwards);
  2236. end;
  2237. function TPascalReaderTool.FindClassOrInterfaceNode(CursorNode: TCodeTreeNode;
  2238. FindClassOfMethod: boolean): TCodeTreeNode;
  2239. begin
  2240. while CursorNode<>nil do begin
  2241. if CursorNode.Desc in AllClasses then begin
  2242. Result:=CursorNode;
  2243. exit;
  2244. end else if FindClassOfMethod and NodeIsMethodBody(CursorNode) then begin
  2245. Result:=FindClassNodeForMethodBody(CursorNode,true,false);
  2246. exit;
  2247. end;
  2248. CursorNode:=CursorNode.Parent;
  2249. end;
  2250. Result:=nil;
  2251. end;
  2252. function TPascalReaderTool.FindClassSection(ClassNode: TCodeTreeNode;
  2253. NodeDesc: TCodeTreeNodeDesc): TCodeTreeNode;
  2254. begin
  2255. Result:=ClassNode.FirstChild;
  2256. while (Result<>nil) and (Result.Desc<>NodeDesc) do
  2257. Result:=Result.NextBrother;
  2258. end;
  2259. function TPascalReaderTool.FindLastClassSection(ClassNode: TCodeTreeNode;
  2260. NodeDesc: TCodeTreeNodeDesc): TCodeTreeNode;
  2261. begin
  2262. Result:=ClassNode.LastChild;
  2263. while (Result<>nil) and (Result.Desc<>NodeDesc) do
  2264. Result:=Result.PriorBrother;
  2265. end;
  2266. function TPascalReaderTool.GetClassVisibility(Node: TCodeTreeNode
  2267. ): TCodeTreeNodeDesc;
  2268. begin
  2269. Result:=ctnNone;
  2270. if Node=nil then exit;
  2271. if Node.Desc=ctnProcedureHead then
  2272. Node:=Node.Parent;
  2273. if not (Node.Desc in AllClassSections) then begin
  2274. Node:=Node.Parent;
  2275. if Node=nil then exit;
  2276. end;
  2277. if Node.Desc in AllClassSubSections then
  2278. Node:=Node.Parent;
  2279. if Node.Desc in AllClassBaseSections then
  2280. Result:=Node.Desc;
  2281. end;
  2282. function TPascalReaderTool.FindClassNodeInInterface(
  2283. const AClassName: string; IgnoreForwards, IgnoreNonForwards,
  2284. ErrorOnNotFound: boolean): TCodeTreeNode;
  2285. procedure RaiseClassNotFound;
  2286. begin
  2287. RaiseExceptionFmt(ctsClassSNotFound, [AClassName]);
  2288. end;
  2289. begin
  2290. Result:=Tree.Root;
  2291. if Result<>nil then begin
  2292. if Result.Desc=ctnUnit then
  2293. Result:=Result.NextBrother;
  2294. if Result<>nil then begin
  2295. Result:=FindClassNode(Result.FirstChild,AClassName,
  2296. IgnoreForwards, IgnoreNonForwards);
  2297. if (Result<>nil) and Result.HasParentOfType(ctnImplementation) then
  2298. Result:=nil;
  2299. end;
  2300. end;
  2301. if (Result=nil) and ErrorOnNotFound then
  2302. RaiseClassNotFound;
  2303. end;
  2304. function TPascalReaderTool.FindClassNodeInUnit(const AClassName: string;
  2305. IgnoreForwards, IgnoreNonForwards, IgnoreImplementation,
  2306. ErrorOnNotFound: boolean): TCodeTreeNode;
  2307. procedure RaiseClassNotFound;
  2308. begin
  2309. RaiseExceptionFmt(ctsClassSNotFound, [AClassName]);
  2310. end;
  2311. begin
  2312. Result:=Tree.Root;
  2313. if Result<>nil then begin
  2314. if Result.Desc in [ctnUnit,ctnLibrary,ctnPackage] then begin
  2315. Result:=Result.NextBrother;
  2316. end;
  2317. if Result<>nil then begin
  2318. Result:=FindClassNode(Result.FirstChild,AClassName,
  2319. IgnoreForwards, IgnoreNonForwards);
  2320. if (Result<>nil) and IgnoreImplementation
  2321. and Result.HasParentOfType(ctnImplementation) then
  2322. Result:=nil;
  2323. end;
  2324. end;
  2325. if (Result=nil) and ErrorOnNotFound then
  2326. RaiseClassNotFound;
  2327. end;
  2328. function TPascalReaderTool.FindFirstIdentNodeInClass(ClassNode: TCodeTreeNode
  2329. ): TCodeTreeNode;
  2330. begin
  2331. if (ClassNode=nil) then exit(nil);
  2332. Result:=FindNextIdentNodeInClass(ClassNode.FirstChild);
  2333. end;
  2334. function TPascalReaderTool.FindLastIdentNodeInClass(ClassNode: TCodeTreeNode
  2335. ): TCodeTreeNode;
  2336. begin
  2337. if (ClassNode=nil) then exit(nil);
  2338. Result:=ClassNode.LastChild;
  2339. if Result=nil then exit;
  2340. while (Result.FirstChild<>nil) and (Result.Desc in AllClassSections) do
  2341. Result:=Result.LastChild;
  2342. if not (Result.Desc in AllClassSections) then
  2343. Result:=FindPriorIdentNodeInClass(Result);
  2344. end;
  2345. function TPascalReaderTool.FindNextIdentNodeInClass(Node: TCodeTreeNode
  2346. ): TCodeTreeNode;
  2347. // Node must be nil or a class section or an identifier node in a class
  2348. begin
  2349. Result:=Node;
  2350. if Result=nil then exit;
  2351. repeat
  2352. // descend into class sections, skip empty class sections
  2353. if (Result.FirstChild<>nil) and (Result.Desc in AllClassSections) then
  2354. Result:=Result.FirstChild
  2355. else begin
  2356. while Result.NextBrother=nil do begin
  2357. Result:=Result.Parent;
  2358. if (Result=nil) or (not (Result.Desc in AllClassSections)) then
  2359. exit(nil);
  2360. end;
  2361. Result:=Result.NextBrother
  2362. end;
  2363. until not (Result.Desc in AllClassSections);
  2364. end;
  2365. function TPascalReaderTool.FindPriorIdentNodeInClass(Node: TCodeTreeNode
  2366. ): TCodeTreeNode;
  2367. begin
  2368. Result:=Node;
  2369. if Result=nil then exit;
  2370. repeat
  2371. if Result.PriorBrother<>nil then begin
  2372. Result:=Result.PriorBrother;
  2373. while (Result.LastChild<>nil) and (Result.Desc in AllClassSections) do
  2374. Result:=Result.LastChild;
  2375. end else if Result.Parent.Desc in AllClassSections then
  2376. Result:=Result.Parent
  2377. else
  2378. exit(nil);
  2379. until not (Result.Desc in AllClassSections);
  2380. end;
  2381. function TPascalReaderTool.ClassSectionNodeStartsWithWord(ANode: TCodeTreeNode
  2382. ): boolean;
  2383. begin
  2384. Result:=(ANode<>nil) and (ANode.StartPos<ANode.EndPos)
  2385. and (IsIdentStartChar[Src[ANode.StartPos]]);
  2386. end;
  2387. function TPascalReaderTool.IsClassNode(Node: TCodeTreeNode): boolean;
  2388. begin
  2389. Result:=(Node<>nil) and (Node.Desc=ctnClass);
  2390. end;
  2391. function TPascalReaderTool.FindInheritanceNode(ClassNode: TCodeTreeNode): TCodeTreeNode;
  2392. begin
  2393. Result:=ClassNode.FirstChild;
  2394. while (Result<>nil) and (Result.Desc in [ctnClassSealed,ctnClassAbstract,ctnClassExternal]) do
  2395. Result:=Result.NextBrother;
  2396. if (Result<>nil) and (Result.Desc<>ctnClassInheritance) then
  2397. Result:=nil;
  2398. end;
  2399. function TPascalReaderTool.ExtractRecordCaseType(RecordCaseNode: TCodeTreeNode): string;
  2400. // case a:b.c of
  2401. // case a:(b,c) of
  2402. var
  2403. VarNode: TCodeTreeNode;
  2404. begin
  2405. Result:='';
  2406. VarNode:=RecordCaseNode.FirstChild;
  2407. if VarNode=nil then exit;
  2408. if VarNode.FirstChild<>nil then
  2409. Result:=ExtractNode(RecordCaseNode.FirstChild,[]);
  2410. end;
  2411. function TPascalReaderTool.GetSourceType: TCodeTreeNodeDesc;
  2412. begin
  2413. if Tree.Root<>nil then
  2414. Result:=Tree.Root.Desc
  2415. else
  2416. Result:=ctnNone;
  2417. end;
  2418. function TPascalReaderTool.IdentNodeIsInVisibleClassSection(
  2419. Node: TCodeTreeNode; Visibility: TClassSectionVisibility): Boolean;
  2420. begin
  2421. if Visibility = csvEverything then
  2422. Result := True
  2423. else
  2424. if (Node.Parent<>nil) then
  2425. case Visibility of
  2426. //csvAbovePrivate: todo: add strict private and strict protected (should be registered as new sections)
  2427. csvProtectedAndHigher:
  2428. Result := not(Node.Parent.Desc = ctnClassPrivate);//todo: add strict private
  2429. csvPublicAndHigher:
  2430. Result := not(Node.Parent.Desc in [ctnClassPrivate, ctnClassProtected]);//todo: strict private and strict protected
  2431. else
  2432. Result := True
  2433. end
  2434. else
  2435. Result := False;
  2436. end;
  2437. function TPascalReaderTool.ExtractProcedureGroup(ProcNode: TCodeTreeNode
  2438. ): TPascalMethodGroup;
  2439. begin
  2440. Result:=mgMethod;
  2441. if (ProcNode=nil) then exit;
  2442. if ProcNode.Desc=ctnProcedureHead then
  2443. ProcNode:=ProcNode.Parent;
  2444. if ProcNode.Desc<>ctnProcedure then exit;
  2445. MoveCursorToNodeStart(ProcNode);
  2446. ReadNextAtom;
  2447. if UpAtomIs('CLASS') then
  2448. begin
  2449. ReadNextAtom;
  2450. if UpAtomIs('CONSTRUCTOR') then
  2451. Result := mgClassConstructor
  2452. else if UpAtomIs('DESTRUCTOR') then
  2453. Result := mgClassDestructor
  2454. else if UpAtomIs('OPERATOR') then
  2455. Result := mgClassOperator;
  2456. end else
  2457. if UpAtomIs('CONSTRUCTOR') then
  2458. Result := mgConstructor
  2459. end;
  2460. function TPascalReaderTool.PositionInSourceName(CleanPos: integer): boolean;
  2461. var
  2462. NamePos: TAtomPosition;
  2463. begin
  2464. Result:=false;
  2465. if not GetSourceNamePos(NamePos) then exit;
  2466. Result:=(CleanPos>=NamePos.StartPos) and (CleanPos<NamePos.EndPos);
  2467. end;
  2468. function TPascalReaderTool.ExtractSourceName: string;
  2469. begin
  2470. Result:='';
  2471. if Tree.Root<>nil then begin
  2472. MoveCursorToNodeStart(Tree.Root);
  2473. ReadNextAtom; // read source type 'program', 'unit' ...
  2474. if (Tree.Root.Desc<>ctnProgram) or UpAtomIs('PROGRAM') then begin
  2475. ReadNextAtom; // read name
  2476. if AtomIsIdentifier then begin
  2477. Result:=copy(Src,CurPos.StartPos,CurPos.EndPos-CurPos.StartPos);
  2478. ReadNextAtom;
  2479. while CurPos.Flag=cafPoint do begin
  2480. ReadNextAtom;
  2481. if not AtomIsIdentifier then exit;
  2482. Result:=Result+'.'+copy(Src,CurPos.StartPos,CurPos.EndPos-CurPos.StartPos);
  2483. ReadNextAtom;
  2484. end;
  2485. exit;
  2486. end;
  2487. end;
  2488. end;
  2489. if (Tree.Root<>nil) and (Tree.Root.Desc=ctnProgram) then
  2490. // a program without the 'program' header uses the file name as name
  2491. Result:=ExtractFileNameOnly(MainFilename)
  2492. else
  2493. Result:='';
  2494. end;
  2495. function TPascalReaderTool.GetSourceNamePos(out NamePos: TAtomPosition
  2496. ): boolean;
  2497. begin
  2498. Result:=false;
  2499. NamePos.StartPos:=-1;
  2500. if Tree.Root=nil then exit;
  2501. MoveCursorToNodeStart(Tree.Root);
  2502. ReadNextAtom; // read source type 'program', 'unit' ...
  2503. if (Tree.Root.Desc=ctnProgram) and (not UpAtomIs('PROGRAM')) then exit;
  2504. ReadNextAtom; // read name
  2505. if not AtomIsIdentifier then exit;
  2506. NamePos:=CurPos;
  2507. Result:=true;
  2508. ReadNextAtom;
  2509. while CurPos.Flag=cafPoint do begin
  2510. ReadNextAtom;
  2511. if not AtomIsIdentifier then exit;
  2512. NamePos.EndPos:=CurPos.EndPos;
  2513. ReadNextAtom;
  2514. end;
  2515. end;
  2516. function TPascalReaderTool.GetSourceName(DoBuildTree: boolean): string;
  2517. begin
  2518. Result:='';
  2519. if DoBuildTree then
  2520. BuildTree(lsrSourceName);
  2521. CachedSourceName:=ExtractSourceName;
  2522. Result:=CachedSourceName;
  2523. end;
  2524. function TPascalReaderTool.NodeIsInAMethod(Node: TCodeTreeNode): boolean;
  2525. begin
  2526. Result:=false;
  2527. while (Node<>nil) do begin
  2528. if (Node.Desc=ctnProcedure) then begin
  2529. if NodeIsMethodBody(Node) then begin
  2530. Result:=true;
  2531. exit;
  2532. end;
  2533. end;
  2534. Node:=Node.Parent;
  2535. end;
  2536. end;
  2537. function TPascalReaderTool.NodeIsMethodBody(ProcNode: TCodeTreeNode): boolean;
  2538. begin
  2539. Result:=false;
  2540. if (ProcNode<>nil) and (ProcNode.Desc=ctnProcedure)
  2541. and (ProcNode.FirstChild<>nil) then begin
  2542. // ToDo: ppu, dcu
  2543. MoveCursorToNodeStart(ProcNode.FirstChild); // ctnProcedureHead
  2544. ReadNextAtom;
  2545. if not AtomIsIdentifier then exit;
  2546. ReadNextAtom;
  2547. if (CurPos.Flag<>cafPoint) then exit;
  2548. Result:=true;
  2549. exit;
  2550. end;
  2551. end;
  2552. function TPascalReaderTool.GetMethodOfBody(Node: TCodeTreeNode): TCodeTreeNode;
  2553. begin
  2554. Result:=Node;
  2555. while (Result<>nil) and not NodeIsMethodBody(Result) do
  2556. Result:=Result.Parent;
  2557. end;
  2558. function TPascalReaderTool.NodeIsFunction(ProcNode: TCodeTreeNode): boolean;
  2559. begin
  2560. Result:=false;
  2561. if (ProcNode=nil) or (ProcNode.Desc<>ctnProcedure) then exit;
  2562. MoveCursorToNodeStart(ProcNode);
  2563. ReadNextAtom;
  2564. if UpAtomIs('CLASS') then ReadNextAtom;
  2565. Result:=UpAtomIs('FUNCTION');
  2566. end;
  2567. function TPascalReaderTool.NodeIsConstructor(ProcNode: TCodeTreeNode): boolean;
  2568. begin
  2569. Result:=false;
  2570. if (ProcNode=nil) then exit;
  2571. if ProcNode.Desc=ctnProcedureHead then
  2572. ProcNode:=ProcNode.Parent;
  2573. if ProcNode.Desc<>ctnProcedure then exit;
  2574. MoveCursorToNodeStart(ProcNode);
  2575. ReadNextAtom;
  2576. if UpAtomIs('CLASS') then ReadNextAtom;
  2577. Result:=UpAtomIs('CONSTRUCTOR');
  2578. if not Result and UpAtomIs('FUNCTION')
  2579. and ([cmsObjectiveC1,cmsObjectiveC2]*Scanner.CompilerModeSwitches<>[]) then
  2580. begin
  2581. ProcNode:=ProcNode.FirstChild;
  2582. if ProcNode=nil then exit;
  2583. if (ProcNode.SubDesc and ctnsNeedJITParsing)>0 then
  2584. BuildSubTreeForProcHead(ProcNode);
  2585. ProcNode:=ProcNode.FirstChild;
  2586. if (ProcNode=nil) then exit;
  2587. if ProcNode.Desc=ctnParameterList then
  2588. ProcNode:=ProcNode.NextBrother;
  2589. if (ProcNode=nil) then exit;
  2590. MoveCursorToNodeStart(ProcNode);
  2591. ReadNextAtom;
  2592. Result:=UpAtomIs('ID');
  2593. end;
  2594. end;
  2595. function TPascalReaderTool.NodeIsDestructor(ProcNode: TCodeTreeNode): boolean;
  2596. begin
  2597. Result:=false;
  2598. if (ProcNode=nil) then exit;
  2599. if ProcNode.Desc=ctnProcedureHead then
  2600. ProcNode:=ProcNode.Parent;
  2601. if ProcNode.Desc<>ctnProcedure then exit;
  2602. MoveCursorToNodeStart(ProcNode);
  2603. ReadNextAtom;
  2604. Result:=UpAtomIs('DESTRUCTOR');
  2605. end;
  2606. function TPascalReaderTool.NodeIsForwardProc(ProcNode: TCodeTreeNode): boolean;
  2607. begin
  2608. Result:=false;
  2609. // check if procedure
  2610. if (ProcNode=nil) or (ProcNode.Desc<>ctnProcedure) then exit;
  2611. // check if in interface
  2612. if (ProcNode.Parent<>nil) and (ProcNode.Parent.Desc=ctnInterface) then
  2613. exit(true);
  2614. // check if has forward
  2615. if (ctnsForwardDeclaration and ProcNode.SubDesc)>0 then exit(true);
  2616. end;
  2617. function TPascalReaderTool.NodeIsOperator(ProcNode: TCodeTreeNode): boolean;
  2618. begin
  2619. Result:=false;
  2620. if (ProcNode=nil) then exit;
  2621. if ProcNode.Desc=ctnProcedureHead then
  2622. ProcNode:=ProcNode.Parent;
  2623. if ProcNode.Desc<>ctnProcedure then exit;
  2624. MoveCursorToNodeStart(ProcNode);
  2625. ReadNextAtom;
  2626. if UpAtomIs('CLASS') then ReadNextAtom;
  2627. Result:=UpAtomIs('OPERATOR');
  2628. end;
  2629. function TPascalReaderTool.NodeIsResultIdentifier(Node: TCodeTreeNode): boolean;
  2630. begin
  2631. Result:=(Node<>nil)
  2632. and (Node.Desc=ctnVarDefinition)
  2633. and (Node.Parent<>nil)
  2634. and (Node.Parent.Desc=ctnProcedureHead);
  2635. end;
  2636. function TPascalReaderTool.NodeIsResultType(Node: TCodeTreeNode): boolean;
  2637. begin
  2638. Result:=(Node<>nil)
  2639. and (Node.Desc=ctnIdentifier)
  2640. and (Node.Parent<>nil)
  2641. and (Node.Parent.Desc=ctnProcedureHead);
  2642. end;
  2643. function TPascalReaderTool.NodeIsPartOfTypeDefinition(ANode: TCodeTreeNode
  2644. ): boolean;
  2645. begin
  2646. ANode:=ANode.Parent;
  2647. while ANode<>nil do begin
  2648. if ANode.Desc in (AllIdentifierDefinitions+AllPascalTypes) then begin
  2649. Result:=true;
  2650. exit;
  2651. end;
  2652. ANode:=ANode.Parent;
  2653. end;
  2654. Result:=false;
  2655. end;
  2656. function TPascalReaderTool.ExtractDefinitionNodeType(
  2657. DefinitionNode: TCodeTreeNode): string;
  2658. var
  2659. TypeNode: TCodeTreeNode;
  2660. begin
  2661. Result:='';
  2662. TypeNode:=FindTypeNodeOfDefinition(DefinitionNode);
  2663. if TypeNode=nil then exit;
  2664. if TypeNode.Desc=ctnIdentifier then
  2665. Result:=GetIdentifier(@Src[TypeNode.StartPos]);
  2666. end;
  2667. function TPascalReaderTool.ExtractFuncResultType(ProcNode: TCodeTreeNode;
  2668. Attr: TProcHeadAttributes): string;
  2669. begin
  2670. Result := '';
  2671. if (ProcNode=nil) then exit;
  2672. if ProcNode.Desc=ctnProcedure then
  2673. ProcNode:=ProcNode.FirstChild;
  2674. if (ProcNode=nil) or(ProcNode.Desc<>ctnProcedureHead) then
  2675. Exit;
  2676. MoveCursorToCleanPos(ProcNode.EndPos);
  2677. CurNode:=ProcNode;
  2678. ReadPriorAtom;
  2679. if CurPos.Flag<>cafSemicolon then
  2680. Exit;
  2681. ReadPriorAtom;
  2682. if CurPos.Flag<>cafWord then
  2683. Exit;
  2684. if phpInUpperCase in Attr then
  2685. Result := GetUpAtom
  2686. else
  2687. Result := GetAtom;
  2688. end;
  2689. function TPascalReaderTool.ExtractDefinitionName(DefinitionNode: TCodeTreeNode
  2690. ): string;
  2691. begin
  2692. DefinitionNode:=FindDefinitionNameNode(DefinitionNode);
  2693. if DefinitionNode<>nil then
  2694. Result:=GetIdentifier(@Src[DefinitionNode.StartPos])
  2695. else
  2696. Result:='';
  2697. end;
  2698. function TPascalReaderTool.PositionInDefinitionName(
  2699. DefinitionNode: TCodeTreeNode; CleanPos: integer): boolean;
  2700. var
  2701. StartPos: LongInt;
  2702. begin
  2703. if DefinitionNode.Desc=ctnGenericType then begin
  2704. if DefinitionNode.FirstChild<>nil then
  2705. StartPos:=DefinitionNode.FirstChild.StartPos
  2706. else
  2707. StartPos:=0;
  2708. end else begin
  2709. StartPos:=DefinitionNode.StartPos;
  2710. end;
  2711. Result:=(CleanPos>=StartPos) and (CleanPos<StartPos+GetIdentLen(@Src[StartPos]));
  2712. end;
  2713. function TPascalReaderTool.MoveCursorToParameterSpecifier(
  2714. DefinitionNode: TCodeTreeNode): boolean;
  2715. begin
  2716. Result:=false;
  2717. if (DefinitionNode=nil) or (DefinitionNode.Desc<>ctnVarDefinition)
  2718. or (DefinitionNode.Parent=nil)
  2719. or (DefinitionNode.Parent.Desc<>ctnParameterList) then exit;
  2720. // find first variable node of this type (e.g. var a,b,c,d: integer)
  2721. DefinitionNode:=GetFirstGroupVarNode(DefinitionNode);
  2722. if DefinitionNode.PriorBrother<>nil then
  2723. MoveCursorToCleanPos(DefinitionNode.PriorBrother.EndPos)
  2724. else
  2725. MoveCursorToCleanPos(DefinitionNode.Parent.StartPos);
  2726. ReadNextAtom;
  2727. while (CurPos.StartPos<DefinitionNode.StartPos) do ReadNextAtom;
  2728. UndoReadNextAtom;
  2729. Result:=CurPos.Flag=cafWord;
  2730. end;
  2731. function TPascalReaderTool.GetFirstGroupVarNode(VarNode: TCodeTreeNode): TCodeTreeNode;
  2732. begin
  2733. Result:=VarNode;
  2734. if (VarNode=nil) or (VarNode.Desc<>ctnVarDefinition) then exit;
  2735. while VarNode<>nil do begin
  2736. VarNode:=VarNode.PriorBrother;
  2737. if (VarNode=nil) or (VarNode.Desc<>ctnVarDefinition)
  2738. or (VarNode.FirstChild<>nil) then exit;
  2739. Result:=VarNode;
  2740. end;
  2741. end;
  2742. function TPascalReaderTool.FindEndOfWithExpr(WithVarNode: TCodeTreeNode): integer;
  2743. begin
  2744. if WithVarNode.Desc<>ctnWithVariable then exit(-1);
  2745. MoveCursorToCleanPos(WithVarNode.StartPos);
  2746. ReadNextAtom;
  2747. if not ReadTilVariableEnd(true,true) then exit(-1);
  2748. UndoReadNextAtom;
  2749. Result:=CurPos.EndPos;
  2750. end;
  2751. function TPascalReaderTool.ExtractWithBlockExpression(
  2752. WithVarNode: TCodeTreeNode; Attr: TProcHeadAttributes): string;
  2753. var
  2754. EndPos: Integer;
  2755. begin
  2756. EndPos:=FindEndOfWithExpr(WithVarNode);
  2757. if EndPos<1 then exit('');
  2758. Result:=ExtractCode(WithVarNode.StartPos,EndPos,Attr);
  2759. end;
  2760. function TPascalReaderTool.FindWithBlockStatement(WithVarNode: TCodeTreeNode
  2761. ): TCodeTreeNode;
  2762. begin
  2763. Result:=WithVarNode;
  2764. repeat
  2765. if Result=nil then exit;
  2766. if Result.Desc<>ctnWithVariable then exit(nil);
  2767. if Result.FirstChild<>nil then begin
  2768. Result:=Result.FirstChild;
  2769. if Result.Desc=ctnWithStatement then exit;
  2770. exit(nil);
  2771. end;
  2772. until false;
  2773. end;
  2774. function TPascalReaderTool.NodeIsIdentifierInInterface(Node: TCodeTreeNode): boolean;
  2775. // true if identifier is visible from other units (without prefixing)
  2776. begin
  2777. case Node.Desc of
  2778. ctnEnumIdentifier:
  2779. Result:=true;
  2780. ctnVarDefinition:
  2781. Result:=(Node.Parent.Desc=ctnVarSection)
  2782. and (Node.Parent.Parent.Desc=ctnInterface);
  2783. ctnConstDefinition:
  2784. Result:=(Node.Parent.Desc=ctnConstSection)
  2785. and (Node.Parent.Parent.Desc=ctnInterface);
  2786. ctnTypeDefinition,ctnGenericType:
  2787. Result:=(Node.Parent.Desc=ctnTypeSection)
  2788. and (Node.Parent.Parent.Desc=ctnInterface);
  2789. ctnProcedure,ctnProperty:
  2790. Result:=Node.Parent.Desc=ctnInterface;
  2791. ctnProcedureHead:
  2792. Result:=(Node.Parent.Desc=ctnProcedure)
  2793. and (Node.Parent.Parent.Desc=ctnInterface);
  2794. end;
  2795. Result:=false;
  2796. end;
  2797. function TPascalReaderTool.NodeCanHaveForwardType(TypeNode: TCodeTreeNode): boolean;
  2798. begin
  2799. Result:=false;
  2800. if (TypeNode=nil) or (TypeNode.Desc<>ctnTypeDefinition)
  2801. or (TypeNode.FirstChild=nil) then
  2802. exit;
  2803. if (TypeNode.FirstChild.Desc in AllClasses)
  2804. and (TypeNode.FirstChild.SubDesc and ctnsForwardDeclaration=0) then
  2805. Result:=true;
  2806. end;
  2807. function TPascalReaderTool.NodeIsClassConstructorOrDestructor(
  2808. ProcNode: TCodeTreeNode): boolean;
  2809. begin
  2810. Result := ExtractProcedureGroup(ProcNode) in [mgClassConstructor, mgClassDestructor];
  2811. end;
  2812. function TPascalReaderTool.NodeIsForwardType(TypeNode: TCodeTreeNode): boolean;
  2813. begin
  2814. Result:=false;
  2815. if (TypeNode=nil) or (TypeNode.Desc<>ctnTypeDefinition)
  2816. or (TypeNode.FirstChild=nil) then
  2817. exit;
  2818. if (TypeNode.FirstChild.Desc in AllClasses)
  2819. and (TypeNode.FirstChild.SubDesc and ctnsForwardDeclaration>0) then
  2820. Result:=true;
  2821. end;
  2822. function TPascalReaderTool.FindForwardTypeNode(TypeNode: TCodeTreeNode;
  2823. SearchFirst: boolean): TCodeTreeNode;
  2824. { Find the first forward type of TypeNode
  2825. }
  2826. function Next: TCodeTreeNode;
  2827. begin
  2828. Result:=FindForwardTypeNode;
  2829. if Result.PriorBrother<>nil then
  2830. // search upwards
  2831. Result:=Result.PriorBrother
  2832. else if Result.Parent.Desc in AllDefinitionSections then begin
  2833. // type section was searched
  2834. // check for other type sections in front
  2835. Result:=Result.Parent;
  2836. repeat
  2837. while (Result.PriorBrother<>nil) do begin
  2838. Result:=Result.PriorBrother;
  2839. if (Result.Desc in AllDefinitionSections)
  2840. and (Result.LastChild<>nil) then begin
  2841. Result:=Result.LastChild;
  2842. exit;
  2843. end;
  2844. end;
  2845. // check if in implementation section
  2846. if (Result.Parent=nil) or (Result.Parent.Desc<>ctnImplementation) then
  2847. exit(nil);
  2848. Result:=Result.Parent;
  2849. // check if there is an interface section
  2850. if (Result.PriorBrother=nil) or (Result.PriorBrother.Desc<>ctnInterface)
  2851. then
  2852. exit(nil);
  2853. // search in interface section
  2854. Result:=Result.PriorBrother;
  2855. Result:=Result.LastChild;
  2856. until Result=nil;
  2857. end else
  2858. exit;
  2859. end;
  2860. var
  2861. Node: TCodeTreeNode;
  2862. begin
  2863. Result:=nil;
  2864. if not NodeCanHaveForwardType(TypeNode) then exit;
  2865. Node:=TypeNode;
  2866. while Node<>nil do begin
  2867. if Node.Desc in AllIdentifierDefinitions then begin
  2868. if CompareIdentifiers(@Src[TypeNode.StartPos],@Src[Node.StartPos])=0
  2869. then begin
  2870. if (Node.Desc=ctnTypeDefinition) and NodeIsForwardType(Node) then begin
  2871. // a forward
  2872. Result:=Node;
  2873. if not SearchFirst then exit;
  2874. end else begin
  2875. // a redefinition
  2876. exit;
  2877. end;
  2878. end;
  2879. end;
  2880. Node:=Next;
  2881. end;
  2882. end;
  2883. function TPascalReaderTool.FindHelperForNode(HelperNode: TCodeTreeNode
  2884. ): TCodeTreeNode;
  2885. begin
  2886. Result:=HelperNode.FirstChild;
  2887. while (Result<>nil) and (Result.Desc = ctnClassInheritance) do
  2888. Result:=Result.NextBrother;
  2889. if (Result<>nil) and (Result.Desc<>ctnHelperFor) then
  2890. Result:=nil;
  2891. end;
  2892. function TPascalReaderTool.FindTypeOfForwardNode(TypeNode: TCodeTreeNode
  2893. ): TCodeTreeNode;
  2894. function Next: TCodeTreeNode;
  2895. begin
  2896. Result:=FindTypeOfForwardNode;
  2897. if Result.NextBrother<>nil then
  2898. // search forwards
  2899. Result:=Result.NextBrother
  2900. else if Result.Parent.Desc in AllDefinitionSections then begin
  2901. // type section was searched
  2902. // check for other type sections in front
  2903. Result:=Result.Parent;
  2904. repeat
  2905. while (Result.NextBrother<>nil) do begin
  2906. Result:=Result.NextBrother;
  2907. if (Result.Desc in AllDefinitionSections)
  2908. and (Result.FirstChild<>nil) then begin
  2909. Result:=Result.FirstChild;
  2910. exit;
  2911. end;
  2912. end;
  2913. // check if in interface section
  2914. if (Result.Parent=nil) or (Result.Parent.Desc<>ctnInterface) then
  2915. exit(nil);
  2916. Result:=Result.Parent;
  2917. // check if there is an implementation section
  2918. if (Result.NextBrother=nil) or (Result.NextBrother.Desc<>ctnImplementation)
  2919. then
  2920. exit(nil);
  2921. // search in implementation section
  2922. Result:=Result.NextBrother;
  2923. Result:=Result.FirstChild;
  2924. until Result=nil;
  2925. end else
  2926. exit;
  2927. end;
  2928. var
  2929. Node: TCodeTreeNode;
  2930. begin
  2931. Result:=nil;
  2932. if not NodeIsForwardType(TypeNode) then exit;
  2933. Node:=TypeNode;
  2934. while Node<>nil do begin
  2935. if Node.Desc in AllIdentifierDefinitions then begin
  2936. if CompareIdentifiers(@Src[TypeNode.StartPos],@Src[Node.StartPos])=0
  2937. then begin
  2938. if (Node.Desc=ctnTypeDefinition) and (not NodeIsForwardType(Node)) then
  2939. begin
  2940. // a type
  2941. Result:=Node;
  2942. exit;
  2943. end else begin
  2944. // a redefinition
  2945. exit;
  2946. end;
  2947. end;
  2948. end;
  2949. Node:=Next;
  2950. end;
  2951. end;
  2952. function TPascalReaderTool.ExtractArrayRange(ArrayNode: TCodeTreeNode;
  2953. Attr: TProcHeadAttributes): string;
  2954. begin
  2955. Result:='';
  2956. if (ArrayNode=nil) or (ArrayNode.Desc<>ctnRangedArrayType) then exit;
  2957. MoveCursorToNodeStart(ArrayNode);
  2958. if not ReadNextUpAtomIs('ARRAY') then exit;
  2959. if not ReadNextAtomIsChar('[') then exit;
  2960. Result:=ExtractBrackets(CurPos.StartPos,Attr);
  2961. end;
  2962. function TPascalReaderTool.PropertyIsDefault(PropertyNode: TCodeTreeNode): boolean;
  2963. begin
  2964. Result:=false;
  2965. if (PropertyNode=nil) or (PropertyNode.Desc<>ctnProperty) then exit;
  2966. MoveCursorToCleanPos(PropertyNode.EndPos);
  2967. ReadPriorAtom;
  2968. if (CurPos.Flag<>cafSemicolon) then exit;
  2969. ReadPriorAtom;
  2970. Result:=UpAtomIs('DEFAULT');
  2971. end;
  2972. function TPascalReaderTool.PropertyNodeHasParamList(PropNode: TCodeTreeNode
  2973. ): boolean;
  2974. begin
  2975. // ToDo: ppu, dcu
  2976. Result:=false;
  2977. if not MoveCursorToPropName(PropNode) then exit;
  2978. ReadNextAtom;
  2979. Result:=(CurPos.Flag=cafEdgedBracketOpen);
  2980. end;
  2981. function TPascalReaderTool.PropNodeIsTypeLess(PropNode: TCodeTreeNode): boolean;
  2982. begin
  2983. // ToDo: ppu, dcu
  2984. Result:=false;
  2985. if PropNode.Desc<>ctnProperty then exit;
  2986. if not MoveCursorToPropName(PropNode) then exit;
  2987. ReadNextAtom; // read colon, skip parameters
  2988. if CurPos.Flag=cafEdgedBracketOpen then begin
  2989. ReadTilBracketClose(true);
  2990. ReadNextAtom;
  2991. end;
  2992. Result:=(CurPos.Flag<>cafColon);
  2993. end;
  2994. function TPascalReaderTool.PropertyHasSpecifier(PropNode: TCodeTreeNode;
  2995. UpperKeyword: string; ExceptionOnNotFound: boolean): boolean;
  2996. // true if cursor is on keyword
  2997. begin
  2998. // ToDo: ppu, dcu
  2999. Result:=false;
  3000. if not MoveCursorToPropName(PropNode) then exit;
  3001. if not AtomIsIdentifierE(ExceptionOnNotFound) then exit;
  3002. ReadNextAtom;
  3003. if CurPos.Flag=cafEdgedBracketOpen then begin
  3004. if not ReadTilBracketClose(ExceptionOnNotFound) then exit;
  3005. ReadNextAtom;
  3006. end;
  3007. if CurPos.Flag=cafColon then begin
  3008. // read type
  3009. ReadNextAtom;
  3010. if not AtomIsIdentifierE(ExceptionOnNotFound) then exit;
  3011. ReadNextAtom;
  3012. if CurPos.Flag=cafPoint then begin
  3013. ReadNextAtom;
  3014. if not AtomIsIdentifierE(ExceptionOnNotFound) then exit;
  3015. ReadNextAtom;
  3016. end;
  3017. end;
  3018. UpperKeyword:=UpperCaseStr(UpperKeyword);
  3019. // read specifiers
  3020. while not (CurPos.Flag in [cafSemicolon,cafNone]) do begin
  3021. if WordIsPropertySpecifier.DoIdentifier(@Src[CurPos.StartPos])
  3022. then begin
  3023. if UpAtomIs(UpperKeyword) then exit(true);
  3024. end else if CurPos.Flag=cafEdgedBracketOpen then begin
  3025. if not ReadTilBracketClose(ExceptionOnNotFound) then exit;
  3026. ReadNextAtom;
  3027. end;
  3028. ReadNextAtom;
  3029. end;
  3030. // read modifiers
  3031. while CurPos.Flag=cafSemicolon do begin
  3032. ReadNextAtom;
  3033. if UpAtomIs('DEFAULT') or UpAtomIs('NODEFAULT') or UpAtomIs('DEPRECATED')
  3034. then begin
  3035. if CompareIdentifierPtrs(@Src[CurPos.StartPos],Pointer(UpperKeyword))=0 then exit(true);
  3036. end else if UpAtomIs('ENUMERATOR') then begin
  3037. if CompareIdentifierPtrs(@Src[CurPos.StartPos],Pointer(UpperKeyword))=0 then exit(true);
  3038. ReadNextAtom;
  3039. if not AtomIsIdentifier then exit;
  3040. end else
  3041. exit;
  3042. ReadNextAtom;
  3043. end;
  3044. end;
  3045. function TPascalReaderTool.ProcNodeHasParamList(ProcNode: TCodeTreeNode): boolean;
  3046. begin
  3047. // ToDo: ppu, dcu
  3048. Result:=false;
  3049. if ProcNode=nil then exit;
  3050. if ProcNode.Desc=ctnProcedure then begin
  3051. ProcNode:=ProcNode.FirstChild;
  3052. if ProcNode=nil then exit;
  3053. end;
  3054. if ProcNode.Desc<>ctnProcedureHead then exit;
  3055. if ProcNode.FirstChild<>nil then begin
  3056. Result:=ProcNode.FirstChild.Desc=ctnParameterList;
  3057. exit;
  3058. end;
  3059. MoveCursorBehindProcName(ProcNode);
  3060. Result:=CurPos.Flag=cafRoundBracketOpen;
  3061. end;
  3062. function TPascalReaderTool.ProcNodeHasOfObject(ProcNode: TCodeTreeNode
  3063. ): boolean;
  3064. begin
  3065. // ToDo: ppu, dcu
  3066. Result:=false;
  3067. if (ProcNode=nil) or (ProcNode.Desc<>ctnProcedureType) then exit;
  3068. MoveCursorToFirstProcSpecifier(ProcNode);
  3069. Result:=UpAtomIs('OF') and ReadNextUpAtomIs('OBJECT');
  3070. end;
  3071. function TPascalReaderTool.GetProcParamList(ProcNode: TCodeTreeNode;
  3072. Parse: boolean): TCodeTreeNode;
  3073. begin
  3074. Result:=ProcNode;
  3075. if Result=nil then exit;
  3076. if Result.Desc=ctnProcedure then begin
  3077. Result:=Result.FirstChild;
  3078. if Result=nil then exit;
  3079. end;
  3080. if Result.Desc<>ctnProcedureHead then exit(nil);
  3081. if Parse then
  3082. BuildSubTreeForProcHead(Result);
  3083. Result:=Result.FirstChild;
  3084. if Result=nil then exit;
  3085. if Result.Desc<>ctnParameterList then exit(nil);
  3086. end;
  3087. procedure TPascalReaderTool.MoveCursorToUsesStart(UsesNode: TCodeTreeNode);
  3088. begin
  3089. if (UsesNode=nil)
  3090. or ((UsesNode.Desc<>ctnUsesSection) and (UsesNode.Desc<>ctnContainsSection))
  3091. then
  3092. RaiseException('[TPascalParserTool.MoveCursorToUsesStart] '
  3093. +'internal error: invalid UsesNode');
  3094. // search through the uses section
  3095. MoveCursorToCleanPos(UsesNode.StartPos);
  3096. ReadNextAtom;
  3097. if (not UpAtomIs('USES')) and (not UpAtomIs('CONTAINS')) then
  3098. RaiseExceptionFmt(ctsStrExpectedButAtomFound,['uses',GetAtom]);
  3099. ReadNextAtom;
  3100. end;
  3101. procedure TPascalReaderTool.MoveCursorToUsesEnd(UsesNode: TCodeTreeNode);
  3102. begin
  3103. if (UsesNode=nil)
  3104. or ((UsesNode.Desc<>ctnUsesSection) and (UsesNode.Desc<>ctnContainsSection))
  3105. then
  3106. RaiseException('[TPascalParserTool.MoveCursorToUsesEnd] '
  3107. +'internal error: invalid UsesNode');
  3108. // search backwards through the uses section
  3109. MoveCursorToCleanPos(UsesNode.EndPos);
  3110. ReadPriorAtom; // read ';'
  3111. if not AtomIsChar(';') then
  3112. RaiseExceptionFmt(ctsStrExpectedButAtomFound,[';',GetAtom]);
  3113. end;
  3114. function TPascalReaderTool.ReadNextUsedUnit(out UnitNameRange,
  3115. InAtom: TAtomPosition; SyntaxExceptions: boolean): boolean;
  3116. // after reading CurPos is on atom behind, i.e. comma or semicolon
  3117. begin
  3118. Result:=false;
  3119. if not AtomIsIdentifierE(SyntaxExceptions) then exit;
  3120. UnitNameRange:=CurPos;
  3121. repeat
  3122. ReadNextAtom;
  3123. if CurPos.Flag<>cafPoint then break;
  3124. ReadNextAtom;
  3125. if not AtomIsIdentifierE(SyntaxExceptions) then exit;
  3126. UnitNameRange.EndPos:=CurPos.EndPos;
  3127. until false;
  3128. if UpAtomIs('IN') then begin
  3129. ReadNextAtom; // read filename
  3130. if not AtomIsStringConstant then begin
  3131. if not SyntaxExceptions then exit;
  3132. RaiseStrConstExpected;
  3133. end;
  3134. InAtom:=CurPos;
  3135. ReadNextAtom; // read comma or semicolon
  3136. end else begin
  3137. InAtom:=CleanAtomPosition;
  3138. end;
  3139. Result:=true;
  3140. end;
  3141. procedure TPascalReaderTool.ReadPriorUsedUnit(out UnitNameRange,InAtom: TAtomPosition);
  3142. begin
  3143. ReadPriorAtom; // read unitname
  3144. if AtomIsStringConstant then begin
  3145. InAtom:=CurPos;
  3146. ReadPriorAtom; // read 'in'
  3147. if not UpAtomIs('IN') then
  3148. RaiseExceptionFmt(ctsStrExpectedButAtomFound,[ctsKeywordIn,GetAtom]);
  3149. ReadPriorAtom; // read unitname
  3150. end else begin
  3151. InAtom:=CleanAtomPosition;
  3152. end;
  3153. AtomIsIdentifierE;
  3154. UnitNameRange:=CurPos;
  3155. repeat
  3156. ReadPriorAtom;
  3157. if CurPos.Flag<>cafPoint then break;
  3158. ReadPriorAtom;
  3159. AtomIsIdentifierE;
  3160. UnitNameRange.StartPos:=CurPos.StartPos;
  3161. until false;
  3162. end;
  3163. function TPascalReaderTool.ExtractUsedUnitNameAtCursor(InFilename: PAnsiString): string;
  3164. begin
  3165. Result:='';
  3166. if InFilename<>nil then
  3167. InFilename^:='';
  3168. while CurPos.Flag=cafWord do begin
  3169. if Result<>'' then
  3170. Result:=Result+'.';
  3171. Result:=Result+GetAtom;
  3172. ReadNextAtom;
  3173. if CurPos.Flag<>cafPoint then break;
  3174. ReadNextAtom;
  3175. end;
  3176. if UpAtomIs('IN') then begin
  3177. ReadNextAtom;
  3178. if not AtomIsStringConstant then exit;
  3179. if InFilename<>nil then
  3180. InFilename^:=copy(Src,CurPos.StartPos+1,CurPos.EndPos-CurPos.StartPos-2);
  3181. ReadNextAtom;
  3182. end;
  3183. end;
  3184. function TPascalReaderTool.ExtractUsedUnitName(UseUnitNode: TCodeTreeNode;
  3185. InFilename: PAnsiString): string;
  3186. // after reading CurPos is on atom behind, i.e. comma or semicolon
  3187. begin
  3188. Result:='';
  3189. if InFilename<>nil then InFilename^:='';
  3190. if (UseUnitNode=nil) or (UseUnitNode.Desc<>ctnUseUnit) then exit;
  3191. MoveCursorToCleanPos(UseUnitNode.StartPos);
  3192. ReadNextAtom;
  3193. Result:=ExtractUsedUnitNameAtCursor(InFilename);
  3194. end;
  3195. function TPascalReaderTool.ReadAndCompareUsedUnit(const AnUnitName: string): boolean;
  3196. // after reading cursor is on atom behind unit name
  3197. var
  3198. p: PChar;
  3199. begin
  3200. Result:=false;
  3201. if IsDottedIdentifier(AnUnitName) then
  3202. p:=PChar(AnUnitName)
  3203. else
  3204. p:=nil;
  3205. repeat
  3206. if not AtomIsIdentifier then exit;
  3207. if (p<>nil) then begin
  3208. if CompareIdentifiers(p,@Src[CurPos.StartPos])=0 then
  3209. inc(p,CurPos.EndPos-CurPos.StartPos)
  3210. else
  3211. p:=nil;
  3212. end;
  3213. ReadNextAtom;
  3214. if CurPos.Flag<>cafPoint then begin
  3215. // end of unit name
  3216. Result:=(p<>nil) and (p^=#0);
  3217. exit;
  3218. end;
  3219. // dot
  3220. if (p<>nil) then begin
  3221. if p='.' then
  3222. inc(p)
  3223. else
  3224. p:=nil;
  3225. end;
  3226. ReadNextAtom;
  3227. until false;
  3228. end;
  3229. function TPascalReaderTool.FindCommentInFront(const StartPos: TCodeXYPosition;
  3230. const CommentText: string; InvokeBuildTree, SearchInParentNode,
  3231. WithCommentBounds, CaseSensitive, IgnoreSpaces, CompareOnlyStart: boolean;
  3232. out CommentStart, CommentEnd: TCodeXYPosition): boolean;
  3233. var
  3234. CleanCursorPos: integer;
  3235. CommentCleanStart: integer;
  3236. CommentCleanEnd: integer;
  3237. begin
  3238. Result:=false;
  3239. if CommentText='' then exit;
  3240. {debugln('TPascalReaderTool.FindCommentInFront A CommentText="',CommentText,'" ',
  3241. ' StartPos=Y='+dbgs(StartPos.Y)+',X='+dbgs(StartPos.X),
  3242. ' InvokeBuildTree='+dbgs(InvokeBuildTree),
  3243. ' SearchInParentNode='+dbgs(SearchInParentNode),
  3244. ' WithCommentBounds='+dbgs(WithCommentBounds),
  3245. ' CaseSensitive='+dbgs(CaseSensitive),
  3246. ' IgnoreSpaces='+dbgs(IgnoreSpaces),
  3247. ' CompareOnlyStart='+dbgs(CompareOnlyStart)); }
  3248. // parse source and find clean positions
  3249. if InvokeBuildTree then
  3250. BuildTreeAndGetCleanPos(StartPos,CleanCursorPos,[])
  3251. else
  3252. if CaretToCleanPos(StartPos,CleanCursorPos)<>0 then
  3253. exit;
  3254. Result:=FindCommentInFront(CleanCursorPos,CommentText,SearchInParentNode,
  3255. WithCommentBounds,CaseSensitive,IgnoreSpaces,CompareOnlyStart,
  3256. CommentCleanStart,CommentCleanEnd);
  3257. if not Result then exit;
  3258. Result:=(CommentCleanStart>=1)
  3259. and CleanPosToCaret(CommentCleanStart,CommentStart)
  3260. and CleanPosToCaret(CommentCleanEnd,CommentEnd);
  3261. end;
  3262. function TPascalReaderTool.FindCommentInFront(StartPos: integer;
  3263. const CommentText: string;
  3264. SearchInParentNode, WithCommentBounds, CaseSensitive,
  3265. IgnoreSpaces, CompareOnlyStart: boolean;
  3266. out CommentStart, CommentEnd: integer): boolean;
  3267. // searches a comment in front of StartPos starting with CommentText.
  3268. var
  3269. FoundStartPos: integer;
  3270. FoundEndPos: integer;
  3271. procedure CompareComment(CStartPos, CEndPos: integer);
  3272. var
  3273. Found: LongInt;
  3274. CompareStartPos: LongInt;
  3275. CompareEndPos: LongInt;
  3276. CompareLen: Integer;
  3277. CompareCommentLength: Integer;
  3278. begin
  3279. //debugln('CompareComment "',copy(Src,CStartPos,CEndPos-CStartPos),'"');
  3280. CompareStartPos:=CStartPos;
  3281. CompareEndPos:=CEndPos;
  3282. if not WithCommentBounds then begin
  3283. // chomp comment boundaries
  3284. case Src[CompareStartPos] of
  3285. '/','(': inc(CompareStartPos,2);
  3286. '{':
  3287. if (CompareStartPos<SrcLen) and (Src[CompareStartPos+1]=#3) then
  3288. // the codetools skip comment is no real comment
  3289. exit
  3290. else
  3291. inc(CompareStartPos,1);
  3292. end;
  3293. case Src[CompareEndPos-1] of
  3294. '}': dec(CompareEndPos);
  3295. ')': dec(CompareEndPos,2);
  3296. #10,#13:
  3297. begin
  3298. dec(CompareEndPos);
  3299. if (Src[CompareEndPos-1] in [#10,#13])
  3300. and (Src[CompareEndPos-1]<>Src[CompareEndPos]) then
  3301. dec(CompareEndPos);
  3302. end;
  3303. end;
  3304. end;
  3305. if CompareStartPos>CompareEndPos then exit;
  3306. if IgnoreSpaces then begin
  3307. while (CompareStartPos<=CompareEndPos)
  3308. and IsSpaceChar[Src[CompareStartPos]]
  3309. do
  3310. inc(CompareStartPos);
  3311. end;
  3312. CompareCommentLength:=length(CommentText);
  3313. CompareLen:=CompareEndPos-CompareStartPos;
  3314. if CompareOnlyStart and (CompareLen>CompareCommentLength) then
  3315. CompareLen:=CompareCommentLength;
  3316. //debugln('Compare: "',copy(Src,CompareStartPos,CompareEndPos-CompareStartPos),'"',
  3317. // ' "',CommentText,'"');
  3318. if IgnoreSpaces then begin
  3319. Found:=CompareTextIgnoringSpace(
  3320. @Src[CompareStartPos],CompareLen,
  3321. @CommentText[1],length(CommentText),
  3322. CaseSensitive);
  3323. end else begin
  3324. Found:=CompareText(@Src[CompareStartPos],CompareLen,
  3325. @CommentText[1],length(CommentText),
  3326. CaseSensitive);
  3327. end;
  3328. if Found=0 then begin
  3329. FoundStartPos:=CStartPos;
  3330. FoundEndPos:=CEndPos;
  3331. end;
  3332. end;
  3333. var
  3334. ANode: TCodeTreeNode;
  3335. p: LongInt;
  3336. CommentStartPos: LongInt;
  3337. begin
  3338. Result:=false;
  3339. if StartPos>SrcLen then
  3340. StartPos:=SrcLen+1;
  3341. if CommentText='' then exit;
  3342. {debugln('TPascalReaderTool.FindCommentInFront A CommentText="',CommentText,'" ',
  3343. ' StartPos=Y='+dbgs(StartPos.Y)+',X='+dbgs(StartPos.X),
  3344. ' InvokeBuildTree='+dbgs(InvokeBuildTree),
  3345. ' SearchInParentNode='+dbgs(SearchInParentNode),
  3346. ' WithCommentBounds='+dbgs(WithCommentBounds),
  3347. ' CaseSensitive='+dbgs(CaseSensitive),
  3348. ' IgnoreSpaces='+dbgs(IgnoreSpaces),
  3349. ' CompareOnlyStart='+dbgs(CompareOnlyStart)); }
  3350. // find node
  3351. ANode:=FindDeepestNodeAtPos(StartPos,true);
  3352. if (ANode=nil) then exit;
  3353. { find end of last atom in front of node
  3354. for example:
  3355. uses classes;
  3356. // Comment
  3357. type
  3358. If ANode is the 'type' block, the position after the semicolon is searched
  3359. }
  3360. if SearchInParentNode and (ANode.Parent<>nil) then begin
  3361. // search all siblings in front
  3362. ANode:=ANode.Parent;
  3363. MoveCursorToCleanPos(ANode.Parent.StartPos);
  3364. end else if ANode.PriorBrother<>nil then begin
  3365. // search between prior sibling and this node
  3366. //DebugLn('TPascalReaderTool.FindCommentInFront ANode.Prior=',ANode.Prior.DescAsString);
  3367. MoveCursorToLastNodeAtom(ANode.PriorBrother);
  3368. end else if ANode.Parent<>nil then begin
  3369. // search from start of parent node to this node
  3370. //DebugLn('TPascalReaderTool.FindCommentInFront ANode.Parent=',ANode.Parent.DescAsString);
  3371. MoveCursorToCleanPos(ANode.Parent.StartPos);
  3372. end else begin
  3373. // search in this node
  3374. //DebugLn('TPascalReaderTool.FindCommentInFront Aode=',ANode.DescAsString);
  3375. MoveCursorToCleanPos(ANode.StartPos);
  3376. end;
  3377. p:=CurPos.EndPos;
  3378. //debugln('TPascalReaderTool.FindCommentInFront B Area="',copy(Src,CurPos.StartPos,StartPos-CurPos.StartPos),'"');
  3379. FoundStartPos:=-1;
  3380. repeat
  3381. //debugln('TPascalReaderTool.FindCommentInFront Atom=',GetAtom);
  3382. CommentStartPos:=FindNextComment(Src,p,StartPos);
  3383. if CommentStartPos>=StartPos then break;
  3384. p:=FindCommentEnd(Src,CommentStartPos,Scanner.NestedComments);
  3385. if p>StartPos then break;
  3386. CompareComment(CommentStartPos,p);
  3387. until false;
  3388. Result:=(FoundStartPos>=1);
  3389. CommentStart:=FoundStartPos;
  3390. CommentEnd:=FoundEndPos;
  3391. end;
  3392. function TPascalReaderTool.GetPasDocComments(const StartPos: TCodeXYPosition;
  3393. InvokeBuildTree: boolean; out ListOfPCodeXYPosition: TFPList): boolean;
  3394. var
  3395. CleanCursorPos: integer;
  3396. ANode: TCodeTreeNode;
  3397. begin
  3398. ListOfPCodeXYPosition:=nil;
  3399. Result:=false;
  3400. // parse source and find clean positions
  3401. if InvokeBuildTree then
  3402. BuildTreeAndGetCleanPos(StartPos,CleanCursorPos)
  3403. else
  3404. if CaretToCleanPos(StartPos,CleanCursorPos)<>0 then
  3405. exit;
  3406. ANode:=FindDeepestNodeAtPos(CleanCursorPos,true);
  3407. Result:=GetPasDocComments(ANode,ListOfPCodeXYPosition);
  3408. end;
  3409. function TPascalReaderTool.GetPasDocComments(Node: TCodeTreeNode;
  3410. out ListOfPCodeXYPosition: TFPList): boolean;
  3411. // Comments are normally in front.
  3412. // { Description of TMyClass. }
  3413. // TMyClass = class
  3414. //
  3415. // Comments can be behind in the same line
  3416. // property Color; // description of Color
  3417. //
  3418. // Comments can be in the following line if started with <
  3419. //
  3420. // comment starting with $ or % are ignored
  3421. function CommentBelongsToPrior(CommentStart: integer): boolean;
  3422. var
  3423. p: Integer;
  3424. begin
  3425. //DebugLn(['CommentBelongsToPrior Comment=',dbgstr(copy(Src,CommentStart,20))]);
  3426. if (CommentStart<SrcLen) and (Src[CommentStart]='{')
  3427. and (Src[CommentStart+1]='<') then
  3428. Result:=true
  3429. else if (CommentStart+2<=SrcLen) and (Src[CommentStart]='(')
  3430. and (Src[CommentStart+1]='*') and (Src[CommentStart+2]='<') then
  3431. Result:=true
  3432. else if (CommentStart+2<=SrcLen) and (Src[CommentStart]='/')
  3433. and (Src[CommentStart+1]='/') and (Src[CommentStart+2]='<') then
  3434. Result:=true
  3435. else begin
  3436. p:=CommentStart-1;
  3437. while (p>=1) and (Src[p] in [' ',#9]) do dec(p);
  3438. //DebugLn(['CommentBelongsToPrior Code in front: ',dbgstr(copy(Src,p,20))]);
  3439. if (p<1) or (Src[p] in [#10,#13]) then
  3440. Result:=false
  3441. else
  3442. Result:=true; // there is code in the same line in front of the comment
  3443. end;
  3444. end;
  3445. procedure Add(CleanPos: integer);
  3446. var
  3447. CodePos: TCodeXYPosition;
  3448. begin
  3449. if not CleanPosToCaret(CleanPos,CodePos) then exit;
  3450. AddCodePosition(ListOfPCodeXYPosition,CodePos);
  3451. end;
  3452. function Scan(StartPos, EndPos: integer): boolean;
  3453. var
  3454. p: LongInt;
  3455. pp: PChar;
  3456. begin
  3457. // read comments (start in front of node)
  3458. //DebugLn(['TPascalReaderTool.GetPasDocComments Scan Src=',copy(Src,StartPos,EndPos-StartPos)]);
  3459. if EndPos>SrcLen then EndPos:=SrcLen+1;
  3460. p:=FindLineEndOrCodeInFrontOfPosition(StartPos,true);
  3461. while p<EndPos do begin
  3462. p:=FindNextComment(Src,p,EndPos);
  3463. if (p>=EndPos) then break;
  3464. pp:=@Src[p];
  3465. if ((pp^='/') and (pp[1]='/') and (pp[2] in ['$','%']))
  3466. or ((pp^='{') and (pp[1] in ['$','%']))
  3467. or ((pp^='(') and (pp[1]='*') and (pp[2] in ['$','%']))
  3468. then
  3469. break;
  3470. //debugln(['TStandardCodeTool.GetPasDocComments Comment="',copy(Src,p,FindCommentEnd(Src,p,Scanner.NestedComments)-p),'"']);
  3471. if (p<StartPos) then begin
  3472. // comment in front of node
  3473. if not CommentBelongsToPrior(p) then
  3474. Add(p);
  3475. end else if (p<EndPos) then begin
  3476. // comment in the middle or behind
  3477. if CommentBelongsToPrior(p) then
  3478. Add(p);
  3479. end;
  3480. p:=FindCommentEnd(Src,p,Scanner.NestedComments);
  3481. end;
  3482. Result:=true;
  3483. end;
  3484. var
  3485. NextNode: TCodeTreeNode;
  3486. EndPos: LongInt;
  3487. TypeNode: TCodeTreeNode;
  3488. begin
  3489. ListOfPCodeXYPosition:=nil;
  3490. Result:=false;
  3491. if (Node=nil) then exit;
  3492. if (Node.Desc=ctnProcedureHead)
  3493. and (Node.Parent<>nil) and (Node.Parent.Desc=ctnProcedure) then
  3494. Node:=Node.Parent;
  3495. // add space behind node to scan range
  3496. NextNode:=Node.Next;
  3497. if NextNode<>nil then
  3498. EndPos:=NextNode.StartPos
  3499. else
  3500. EndPos:=Node.EndPos;
  3501. // scan range for comments
  3502. if not Scan(Node.StartPos,EndPos) then exit;
  3503. if Node.Desc in AllIdentifierDefinitions then begin
  3504. // scan behind type
  3505. // for example: i: integer; // comment
  3506. TypeNode:=FindTypeNodeOfDefinition(Node);
  3507. if TypeNode<>nil then begin
  3508. NextNode:=TypeNode.Next;
  3509. if NextNode<>nil then
  3510. EndPos:=NextNode.StartPos
  3511. else
  3512. EndPos:=Node.EndPos;
  3513. if not Scan(TypeNode.EndPos,EndPos) then exit;
  3514. end;
  3515. end;
  3516. Result:=true;
  3517. end;
  3518. procedure TPascalReaderTool.CalcMemSize(Stats: TCTMemStats);
  3519. begin
  3520. inherited CalcMemSize(Stats);
  3521. Stats.Add('TPascalReaderTool',MemSizeString(CachedSourceName));
  3522. end;
  3523. end.