Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

1829 wiersze
60 KiB

  1. unit HeaderGenerator;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, DOM, uutlGenerics, variants;
  6. type
  7. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  8. THeaderGenerator = class(TObject)
  9. private type
  10. TStringTree = class(TObject)
  11. public type
  12. TItemList = specialize TutlList<TStringTree>;
  13. TItemMap = specialize TutlMap<string, TStringTree>;
  14. private
  15. fLinesBefore: TStringList;
  16. fLinesAfter: TStringList;
  17. fItemList: TItemList;
  18. fItemMap: TItemMap;
  19. function GetItem(const aName: String): TStringTree;
  20. function GetItemCount: Integer;
  21. function GetItemsByIndex(const aIndex: Integer): TStringTree;
  22. function GetLinesAfter: TStrings;
  23. function GetLinesBefore: TStrings;
  24. function GetText: String;
  25. public
  26. property Text: String read GetText;
  27. property LinesBefore: TStrings read GetLinesBefore;
  28. property LinesAfter: TStrings read GetLinesAfter;
  29. property ItemCount: Integer read GetItemCount;
  30. property Items [const aName: String]: TStringTree read GetItem;
  31. property ItemsByIndex[const aIndex: Integer]: TStringTree read GetItemsByIndex;
  32. function IndexOf(const aItem: TStringTree): Integer;
  33. function GetOrCreateItem(const aName: String): TStringTree;
  34. function CreateItemAt(const aName: String; const aIndex: Integer): TStringTree;
  35. function DestroyItem(const aName: String): Boolean;
  36. procedure Clear;
  37. constructor Create;
  38. destructor Destroy; override;
  39. end;
  40. ////////////////////////////////////////////////////////////////////////////////////////////
  41. TvkObjectType = (
  42. otUnknown,
  43. otType,
  44. otHandle,
  45. otEnum,
  46. otConst,
  47. otFuncPtr,
  48. otStruct,
  49. otCommand);
  50. TvkObjectTypes = set of TvkObjectType;
  51. TvkObject = class;
  52. TvkObjectList = class(specialize TutlList<TvkObject>)
  53. public
  54. function IndexOfObj(const aName: DOMstring; const aTypes: TvkObjectTypes): Integer;
  55. function FindObj(const aName: DOMstring; const aTypes: TvkObjectTypes; out aObject): Boolean;
  56. function GetPascalName(const aName: DOMString; const aPointer: Integer; const aArrSize: WideString = ''): WideString;
  57. function GenerateCode(const aName: String; const aCode: TStringTree): Boolean;
  58. procedure AddObject(const aObj: TvkObject);
  59. end;
  60. ////////////////////////////////////////////////////////////////////////////////////////////
  61. TvkObject = class(TObject)
  62. private
  63. fType: TvkObjectType;
  64. fName: DOMString;
  65. fComment: DOMString;
  66. fList: TvkObjectList;
  67. protected
  68. function GetDependencyOrder: Integer; virtual;
  69. procedure GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString); virtual;
  70. {%H-}constructor Create(const aType: TvkObjectType);
  71. public
  72. property VkType: TvkObjectType read fType;
  73. property Name: DOMString read fName write fName;
  74. property Comment: DOMString read fComment write fComment;
  75. property List: TvkObjectList read fList write fList;
  76. function GetPascalName(const aPointer: Integer; const aArrSize: WideString): WideString;
  77. procedure ReadXml(const xml: TDOMNode); virtual;
  78. procedure GenerateCode(const aCode: TStringTree); virtual;
  79. public
  80. class procedure CreateFromXml(const xml: TDOMNode; const aList: TvkObjectList);
  81. end;
  82. ////////////////////////////////////////////////////////////////////////////////////////////
  83. TvkTypeBase = class(TvkObject)
  84. private
  85. fParent: DOMString;
  86. fAlias: DOMString;
  87. protected
  88. function GetDependencyOrder: Integer; override;
  89. procedure GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString); override;
  90. public
  91. property Parent: DOMString read fParent write fParent;
  92. property Alias: DOMString read fAlias write fAlias;
  93. procedure GenerateCode(const aCode: TStringTree); override;
  94. end;
  95. ////////////////////////////////////////////////////////////////////////////////////////////
  96. TvkType = class(TvkTypeBase)
  97. public
  98. procedure ReadXml(const xml: TDOMNode); override;
  99. constructor Create;
  100. end;
  101. ////////////////////////////////////////////////////////////////////////////////////////////
  102. TvkHandle = class(TvkTypeBase)
  103. public
  104. procedure ReadXml(const xml: TDOMNode); override;
  105. constructor Create;
  106. end;
  107. ////////////////////////////////////////////////////////////////////////////////////////////
  108. TvkFunctionBase = class(TvkObject)
  109. private type
  110. TArgument = class(TObject)
  111. Typ: DOMString;
  112. Name: DOMString;
  113. IsPointer: Integer;
  114. IsConst: Boolean;
  115. end;
  116. TArgumentList = specialize TutlList<TArgument>;
  117. private
  118. fReturnValue: TArgument;
  119. fArguments: TArgumentList;
  120. protected
  121. function GetDependencyOrder: Integer; override;
  122. procedure GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString); override;
  123. procedure GenerateFunctionPtr(const aCode, aSub: TStringTree);
  124. {%H-}constructor Create(const aType: TvkObjectType);
  125. public
  126. property ReturnValue: TArgument read fReturnValue;
  127. property Arguments: TArgumentList read fArguments;
  128. destructor Destroy; override;
  129. end;
  130. ////////////////////////////////////////////////////////////////////////////////////////////
  131. TvkFunctionPtr = class(TvkFunctionBase)
  132. public
  133. procedure ReadXml(const xml: TDOMNode); override;
  134. procedure GenerateCode(const aCode: TStringTree); override;
  135. constructor Create;
  136. end;
  137. ////////////////////////////////////////////////////////////////////////////////////////////
  138. TvkCommand = class(TvkFunctionBase)
  139. public
  140. procedure ReadXml(const xml: TDOMNode); override;
  141. procedure GenerateCode(const aCode: TStringTree); override;
  142. constructor Create;
  143. end;
  144. ////////////////////////////////////////////////////////////////////////////////////////////
  145. TvkStruct = class(TvkObject)
  146. private type
  147. TMember = class(TObject)
  148. Typ: DOMString;
  149. Name: DOMString;
  150. ArrSize: DOMString;
  151. IsPointer: Integer;
  152. IsConst: Boolean;
  153. end;
  154. TMemberList = specialize TutlList<TMember>;
  155. private
  156. fIsUnion: Boolean;
  157. fMember: TMemberList;
  158. protected
  159. function GetDependencyOrder: Integer; override;
  160. procedure GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString); override;
  161. public
  162. property Member: TMemberList read fMember;
  163. property IsUnion: Boolean read fIsUnion write fIsUnion;
  164. procedure ReadXml(const xml: TDOMNode); override;
  165. procedure GenerateCode(const aCode: TStringTree); override;
  166. constructor Create;
  167. destructor Destroy; override;
  168. end;
  169. ////////////////////////////////////////////////////////////////////////////////////////////
  170. TvkEnum = class(TvkObject)
  171. private type
  172. TValue = class(TObject)
  173. Name: DOMString;
  174. Value: Variant;
  175. IsBit: Boolean;
  176. end;
  177. TValueList = specialize TutlList<TValue>;
  178. private
  179. fExpands: DOMString;
  180. fValues: TValueList;
  181. protected
  182. procedure GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString); override;
  183. public
  184. property Expands: DOMString read fExpands write fExpands;
  185. property Values: TValueList read fValues;
  186. procedure ReadXml(const xml: TDOMNode); override;
  187. procedure GenerateCode(const aCode: TStringTree); override;
  188. constructor Create;
  189. destructor Destroy; override;
  190. end;
  191. ////////////////////////////////////////////////////////////////////////////////////////////
  192. TvkConst = class(TvkObject)
  193. private
  194. fValue: Variant;
  195. public
  196. property Value: Variant read fValue write fValue;
  197. procedure GenerateCode(const aCode: TStringTree); override;
  198. constructor Create;
  199. end;
  200. private const
  201. VK_OBJECT_TYPES: TvkObjectTypes = [ otType, otHandle, otFuncPtr, otStruct, otEnum ];
  202. VK_OBJECT_ALL: TvkObjectTypes = [ low(TvkObjectType) .. high(TvkObjectType) ];
  203. private
  204. fDocument: TXMLDocument;
  205. fObjects: TvkObjectList;
  206. fComment: WideString;
  207. public
  208. procedure ReadFile(const aFilename: String);
  209. procedure ReadFile(const aStream: TStream);
  210. procedure GenerateCode(const aName: WideString; const aTemplate: String; const aOutput: String);
  211. procedure Clear;
  212. constructor Create;
  213. destructor Destroy; override;
  214. end;
  215. implementation
  216. uses
  217. Math, XMLRead, RegExpr;
  218. type
  219. XmlException = class(Exception)
  220. public
  221. xml: TDOMNode;
  222. constructor Create(const aMsg: WideString; const aXml: TDOMNode);
  223. end;
  224. const
  225. KEYWORD_INTF = 'INTERFACE';
  226. KEYWORD_CONST = 'CONST';
  227. KEYWORD_TYPES = 'TYPES';
  228. KEYWORD_ENUM = 'ENUM';
  229. KEYWORD_ENUM_VALUES = 'ENUM_VALUES';
  230. KEYWORD_STRUCT = 'STRUCT';
  231. KEYWORD_FUNCPTR = 'FUNCPTR';
  232. KEYWORD_COMMAND = 'COMMAND';
  233. KEYWORD_IMPL = 'IMPLEMENTATION';
  234. KEYWORD_COMMENT = 'COMMENT';
  235. KEYWORD_VAR = 'VAR';
  236. KEYWORD_CODE = 'CODE';
  237. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  238. function PointerCount(const aStr: AnsiString): Integer;
  239. var i: Integer;
  240. begin
  241. result := 0;
  242. i := Length(aStr);
  243. while (i > 0) do begin
  244. if (aStr[i] = '*') then
  245. inc(result);
  246. dec(i);
  247. end;
  248. end;
  249. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  250. function StrToValue(s: String): Variant;
  251. var
  252. f: TFormatSettings;
  253. rx: TRegExpr;
  254. begin
  255. f.DecimalSeparator := '.';
  256. rx := TRegExpr.Create;
  257. try
  258. result := Unassigned;
  259. rx.Expression := '^-?([0-9]+)$';
  260. if rx.Exec(s) then
  261. result := StrToInt(rx.Match[0]);
  262. rx.Expression := '^0x([0-9A-Fa-f]+)$';
  263. if rx.Exec(s) then
  264. result := '$' + rx.Match[1];
  265. rx.Expression := '^([0-9\.]+)(f|d)?$';
  266. if rx.Exec(s) then begin
  267. result := StrToFloat(rx.Match[1], f);
  268. if (rx.Match[2] = 'd') then
  269. result := Double(result);
  270. end;
  271. rx.Expression := '^\(~0U([L]*)\)$';
  272. if rx.Exec(s) then begin
  273. if (rx.Match[2] = '')
  274. then result := '$' + IntToHex(High(UInt32), 2 * sizeof(UInt32))
  275. else result := '$' + IntToHex(High(UInt64), 2 * sizeof(UInt64));
  276. end;
  277. finally
  278. FreeAndNil(rx);
  279. end;
  280. end;
  281. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  282. function PrepareName(const aName, aType: WideString): WideString;
  283. var
  284. rx: TRegExpr;
  285. s: WideString;
  286. begin
  287. result := aName;
  288. s := '';
  289. if (result = 'type') then
  290. s := 'Type'
  291. else if (result = 'object') then
  292. s := 'Object';
  293. if (s <> '') then begin
  294. rx := TRegExpr.Create('^(T|P)?(Vk)?(.)');
  295. try
  296. if rx.Exec(String(aType))
  297. then result := WideString(LowerCase(rx.Match[3]) + s)
  298. else result := 'm' + s;
  299. finally
  300. FreeAndNil(rx);
  301. end;
  302. end;
  303. end;
  304. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  305. function FormatType(const aType: THeaderGenerator.TvkObjectType): WideString;
  306. begin
  307. case aType of
  308. otType: result := 'TYPE';
  309. otHandle: result := 'HANDLE';
  310. otFuncPtr: result := 'FUNCPTR';
  311. otStruct: result := 'STRUCT';
  312. otEnum: result := 'ENUM';
  313. otConst: result := 'CONSTANT';
  314. otCommand: result := 'COMMAND';
  315. else
  316. result := '';
  317. end;
  318. end;
  319. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  320. procedure WriteResolveWarning(const aName: DOMString);
  321. begin
  322. WriteLn('WARNING! unable to resolve identifier: ' + aName);
  323. end;
  324. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  325. function GetNodeValue(xml: TDOMNode; const aName: WideString): DOMString;
  326. var x: TDOMNode;
  327. begin
  328. x := xml.FindNode(aName);
  329. if not Assigned(x) then
  330. raise XmlException.Create(xml.NodeName + ' without child ''' + aName + '''', xml);
  331. result := x.TextContent;
  332. end;
  333. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  334. function TryGetNodeValue(xml: TDOMNode; const aName: WideString; out aValue: DOMString): Boolean;
  335. var x: TDOMNode;
  336. begin
  337. x := xml.FindNode(aName);
  338. result := Assigned(x);
  339. if result then
  340. aValue := x.TextContent;
  341. end;
  342. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  343. function GetNodeValue(xml: TDOMNode; const aName: WideString; const aDefault: DOMString): DOMString;
  344. var x: TDOMNode;
  345. begin
  346. x := xml.FindNode(aName);
  347. if not Assigned(x)
  348. then result := aDefault
  349. else result := x.TextContent;
  350. end;
  351. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  352. function GetNodeAttrib(xml: TDOMNode; const aName: WideString): DOMString;
  353. var x: TDOMNode;
  354. begin
  355. if xml.HasAttributes then begin
  356. x := xml.Attributes.GetNamedItem(aName);
  357. if Assigned(x) then begin
  358. result := x.TextContent;
  359. exit;
  360. end;
  361. end;
  362. raise XmlException.Create(xml.NodeName + ' without ''' + aName + ''' attribute', xml);
  363. end;
  364. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  365. function TryGetNodeAttrib(xml: TDOMNode; const aName: WideString; out aValue: DOMString): Boolean;
  366. var x: TDOMNode;
  367. begin
  368. result := true;
  369. if xml.HasAttributes then begin
  370. x := xml.Attributes.GetNamedItem(aName);
  371. if Assigned(x) then begin
  372. aValue := x.TextContent;
  373. exit;
  374. end;
  375. end;
  376. result := false;
  377. end;
  378. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  379. function GetNodeAttrib(xml: TDOMNode; const aName: WideString; const aDefault: DOMString): DOMString;
  380. var x: TDOMNode;
  381. begin
  382. result := aDefault;
  383. if xml.HasAttributes then begin
  384. x := xml.Attributes.GetNamedItem(aName);
  385. if Assigned(x) then
  386. result := x.TextContent;
  387. end;
  388. end;
  389. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  390. //XmlException//////////////////////////////////////////////////////////////////////////////////////////////////////////
  391. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  392. constructor XmlException.Create(const aMsg: WideString; const aXml: TDOMNode);
  393. begin
  394. inherited Create(String(aMsg + ': ' + aXml.TextContent));
  395. xml := aXml;
  396. end;
  397. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  398. //THeaderGenerator.TStringTree//////////////////////////////////////////////////////////////////////////////////////////
  399. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  400. function THeaderGenerator.TStringTree.GetLinesAfter: TStrings;
  401. begin
  402. result := fLinesAfter;
  403. end;
  404. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  405. function THeaderGenerator.TStringTree.GetLinesBefore: TStrings;
  406. begin
  407. result := fLinesBefore;
  408. end;
  409. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  410. function THeaderGenerator.TStringTree.GetItem(const aName: String): TStringTree;
  411. begin
  412. result := fItemMap[aName];
  413. end;
  414. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  415. function THeaderGenerator.TStringTree.GetItemCount: Integer;
  416. begin
  417. result := fItemList.Count;
  418. end;
  419. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  420. function THeaderGenerator.TStringTree.GetItemsByIndex(const aIndex: Integer): TStringTree;
  421. begin
  422. result := fItemList[aIndex];
  423. end;
  424. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  425. function THeaderGenerator.TStringTree.GetText: String;
  426. var
  427. i: Integer;
  428. s: String;
  429. procedure AddText(const aText: String; const aCanAddEmpty: Boolean);
  430. begin
  431. if not aCanAddEmpty and (aText = '') then
  432. exit;
  433. result := result + aText + sLineBreak;
  434. end;
  435. begin
  436. result := '';
  437. for i := 0 to fLinesBefore.Count-1 do
  438. AddText(fLinesBefore[i], true);
  439. for i := 0 to fItemList.Count-1 do begin
  440. s := fItemList[i].Text;
  441. if (UpperCase(Trim(s)) = '{$ENDIF}') then
  442. result := TrimRight(result) + sLineBreak;
  443. AddText(s, false);
  444. end;
  445. for i := 0 to fLinesAfter.Count-1 do
  446. AddText(fLinesAfter[i], true);
  447. result := copy(result, 0, Length(result) - Length(sLineBreak));
  448. end;
  449. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  450. function THeaderGenerator.TStringTree.IndexOf(const aItem: TStringTree): Integer;
  451. begin
  452. result := fItemList.IndexOf(aItem);
  453. end;
  454. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  455. function THeaderGenerator.TStringTree.GetOrCreateItem(const aName: String): TStringTree;
  456. begin
  457. result := GetItem(aName);
  458. if not Assigned(result) then begin
  459. result := TStringTree.Create;
  460. fItemMap.Add(aName, result);
  461. fItemList.Add(result);
  462. end;
  463. end;
  464. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  465. function THeaderGenerator.TStringTree.CreateItemAt(const aName: String; const aIndex: Integer): TStringTree;
  466. begin
  467. result := TStringTree.Create;
  468. fItemMap.Add(aName, result);
  469. fItemList.Insert(aIndex, result);
  470. end;
  471. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  472. function THeaderGenerator.TStringTree.DestroyItem(const aName: String): Boolean;
  473. var t: TStringTree;
  474. begin
  475. t := GetItem(aName);
  476. result := Assigned(t);
  477. if result then begin
  478. fItemList.Remove(t);
  479. fItemMap.Delete(aName);
  480. end;
  481. end;
  482. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  483. procedure THeaderGenerator.TStringTree.Clear;
  484. begin
  485. fItemList.Clear;
  486. fItemMap.Clear;
  487. end;
  488. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  489. constructor THeaderGenerator.TStringTree.Create;
  490. begin
  491. inherited Create;
  492. fLinesBefore := TStringList.Create;
  493. fLinesAfter := TStringList.Create;
  494. fItemList := TItemList.Create(false);
  495. fItemMap := TItemMap.Create(true);
  496. end;
  497. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  498. destructor THeaderGenerator.TStringTree.Destroy;
  499. begin
  500. FreeAndNil(fItemList);
  501. FreeAndNil(fItemMap);
  502. FreeAndNil(fLinesBefore);
  503. FreeAndNil(fLinesAfter);
  504. inherited Destroy;
  505. end;
  506. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  507. //THeaderGenerator.TvkObjectList////////////////////////////////////////////////////////////////////////////////////////
  508. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  509. function THeaderGenerator.TvkObjectList.IndexOfObj(const aName: DOMstring; const aTypes: TvkObjectTypes): Integer;
  510. var o: TvkObject;
  511. begin
  512. if (aName = '') then
  513. raise Exception.Create('empty name');
  514. for result := 0 to Count-1 do begin
  515. o := GetItem(result);
  516. if (o.VkType in aTypes) and (o.Name = aName) then
  517. exit;
  518. end;
  519. result := -1;
  520. end;
  521. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  522. function THeaderGenerator.TvkObjectList.FindObj(const aName: DOMstring; const aTypes: TvkObjectTypes; out aObject): Boolean;
  523. var i: Integer;
  524. begin
  525. i := IndexOfObj(aName, aTypes);
  526. result := (i >= 0);
  527. if result
  528. then TvkObject(aObject) := GetItem(i)
  529. else TvkObject(aObject) := nil;
  530. end;
  531. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  532. function THeaderGenerator.TvkObjectList.GetPascalName(const aName: DOMString; const aPointer: Integer;
  533. const aArrSize: WideString): WideString;
  534. var o: TvkObject;
  535. begin
  536. if FindObj(aName, VK_OBJECT_TYPES, o)
  537. then result := o.GetPascalName(aPointer, aArrSize)
  538. else result := aName;
  539. end;
  540. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  541. function THeaderGenerator.TvkObjectList.GenerateCode(const aName: String; const aCode: TStringTree): Boolean;
  542. var o: TvkObject;
  543. begin
  544. result := FindObj(aName, VK_OBJECT_ALL, o);
  545. if result then
  546. o.GenerateCode(aCode);
  547. end;
  548. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  549. procedure THeaderGenerator.TvkObjectList.AddObject(const aObj: TvkObject);
  550. begin
  551. if Assigned(aObj) then begin
  552. Add(aObj);
  553. aObj.List := self;
  554. WriteLn(Format(' %-10s%s', [FormatType(aObj.VkType), aObj.Name]));
  555. end;
  556. end;
  557. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  558. //THeaderGenerator.TvkObject////////////////////////////////////////////////////////////////////////////////////////////
  559. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  560. function THeaderGenerator.TvkObject.GetDependencyOrder: Integer;
  561. begin
  562. result := 0;
  563. end;
  564. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  565. procedure THeaderGenerator.TvkObject.GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString);
  566. var
  567. i: Integer;
  568. begin
  569. if (aName = '') then begin
  570. aName := fName;
  571. if (aName = 'void') then
  572. aName := 'VkVoid';
  573. for i := 0 to aPointer-1 do
  574. aName := 'P' + aName;
  575. end;
  576. if (aName = 'VkVoid') then
  577. raise Exception.Create('void can not be used as type');
  578. if (aArrSize <> '') then begin
  579. if TryStrToInt(String(aArrSize), i)
  580. then aName := WideString(Format('array[0..%d] of %s', [i, aName]))
  581. else aName := WideString(Format('array[0..%s-1] of %s', [aArrSize, aName]));
  582. end;
  583. end;
  584. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  585. constructor THeaderGenerator.TvkObject.Create(const aType: TvkObjectType);
  586. begin
  587. inherited Create;
  588. fType := aType;
  589. end;
  590. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  591. function THeaderGenerator.TvkObject.GetPascalName(const aPointer: Integer; const aArrSize: WideString): WideString;
  592. begin
  593. result := '';
  594. GenPascalName(result, aPointer, aArrSize);
  595. end;
  596. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  597. procedure THeaderGenerator.TvkObject.ReadXml(const xml: TDOMNode);
  598. begin
  599. // DUMMY
  600. end;
  601. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  602. procedure THeaderGenerator.TvkObject.GenerateCode(const aCode: TStringTree);
  603. begin
  604. // DUMMY
  605. end;
  606. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  607. class procedure THeaderGenerator.TvkObject.CreateFromXml(const xml: TDOMNode; const aList: TvkObjectList);
  608. var
  609. s, n: DOMString;
  610. procedure GenerateVersionEnum;
  611. var
  612. c: TvkConst;
  613. rx: TRegExpr;
  614. begin
  615. rx := TRegExpr.Create('\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)');
  616. try
  617. if not rx.Exec(String(xml.TextContent)) then
  618. raise XmlException.Create('VK_API_VERSION with malformed version', xml);
  619. c := TvkConst.Create;
  620. try
  621. c.Name := 'VK_API_VERSION';
  622. c.Value := (StrToInt(rx.Match[1]) shl 22) or
  623. (StrToInt(rx.Match[2]) shl 12) or
  624. (StrToInt(rx.Match[3]) shl 0);
  625. aList.AddObject(c);
  626. except
  627. FreeAndNil(c);
  628. raise
  629. end;
  630. c := TvkConst.Create;
  631. try
  632. c.Name := 'VK_API_VERSION_MAJOR';
  633. c.Value := StrToInt(rx.Match[1]);
  634. aList.AddObject(c);
  635. except
  636. FreeAndNil(c);
  637. raise
  638. end;
  639. c := TvkConst.Create;
  640. try
  641. c.Name := 'VK_API_VERSION_MINOR';
  642. c.Value := StrToInt(rx.Match[2]);
  643. aList.AddObject(c);
  644. except
  645. FreeAndNil(c);
  646. raise
  647. end;
  648. c := TvkConst.Create;
  649. try
  650. c.Name := 'VK_API_VERSION_BUGFIX';
  651. c.Value := StrToInt(rx.Match[3]);
  652. aList.AddObject(c);
  653. except
  654. FreeAndNil(c);
  655. raise
  656. end;
  657. finally
  658. FreeAndNil(rx);
  659. end;
  660. end;
  661. procedure GenerateConstants;
  662. var
  663. x: TDOMNode;
  664. c: TvkConst;
  665. begin
  666. x := xml.FirstChild;
  667. while Assigned(x) do begin
  668. if (x.NodeName = 'enum') then begin
  669. c := TvkConst.Create;
  670. try
  671. c.Name := GetNodeAttrib(x, 'name');
  672. c.Value := StrToValue(String(GetNodeAttrib(x, 'value')));
  673. if (c.Value = Unassigned) then
  674. raise Exception.Create('Invalid constant value: ' + GetNodeAttrib(x, 'value'));
  675. aList.AddObject(c);
  676. except
  677. FreeAndNil(c);
  678. raise;
  679. end;
  680. end;
  681. x := x.NextSibling;
  682. end;
  683. end;
  684. var
  685. obj: TvkObject;
  686. begin
  687. try
  688. obj := nil;
  689. if (xml.NodeName = 'type') and TryGetNodeAttrib(xml, 'category', s) then begin
  690. if (s = 'basetype') or (s = 'bitmask') then
  691. obj := TvkType.Create
  692. else if (s = 'handle') then
  693. obj := TvkHandle.Create
  694. else if (s = 'funcpointer') then
  695. obj := TvkFunctionPtr.Create
  696. else if (s = 'struct') or (s = 'union') then
  697. obj := TvkStruct.Create
  698. else if (s = 'define') and TryGetNodeValue(xml, 'name', n) and (n = 'VK_API_VERSION') then
  699. GenerateVersionEnum;
  700. end else if (xml.NodeName = 'command') then
  701. obj := TvkCommand.Create
  702. else if (xml.NodeName = 'enums') then begin
  703. if not TryGetNodeAttrib(xml, 'type', s) then
  704. GenerateConstants
  705. else if (s = 'enum') or (s = 'bitmask') then
  706. obj := TvkEnum.Create;
  707. end;
  708. if Assigned(obj) then begin
  709. obj.ReadXml(xml);
  710. aList.AddObject(obj);
  711. end;
  712. except
  713. FreeAndNil(obj);
  714. raise;
  715. end;
  716. end;
  717. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  718. //THeaderGenerator.TvkTypeBase//////////////////////////////////////////////////////////////////////////////////////////
  719. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  720. function THeaderGenerator.TvkTypeBase.GetDependencyOrder: Integer;
  721. begin
  722. result := inherited GetDependencyOrder;
  723. if Assigned(List) and (fParent <> '') then
  724. result := max(result, List.IndexOfObj(fParent, VK_OBJECT_TYPES) + 1);
  725. end;
  726. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  727. procedure THeaderGenerator.TvkTypeBase.GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString);
  728. var i: Integer;
  729. begin
  730. if (fAlias <> '')
  731. then aName := fAlias
  732. else aName := fName;
  733. if (aName = 'void') then
  734. aName := 'VkVoid';
  735. for i := 0 to aPointer-1 do
  736. aName := 'P' + aName;
  737. if (aName = 'VkVoid') then
  738. raise Exception.Create('void can not be used as type');
  739. inherited GenPascalName(aName, aPointer, aArrSize);
  740. end;
  741. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  742. procedure THeaderGenerator.TvkTypeBase.GenerateCode(const aCode: TStringTree);
  743. var st: TStringTree;
  744. begin
  745. inherited GenerateCode(aCode);
  746. if Assigned(fList) and (fParent <> '') then
  747. fList.GenerateCode(fParent, aCode);
  748. if Assigned(fList) and (fAlias <> '') then
  749. fList.GenerateCode(fAlias, aCode);
  750. st := aCode.GetOrCreateItem(KEYWORD_INTF).GetOrCreateItem(KEYWORD_TYPES);
  751. if (st.LinesBefore.Count = 0) then
  752. st.LinesBefore.Add('type { simple types }');
  753. st := st.GetOrCreateItem(fName);
  754. if (st.LinesBefore.Count = 0) and (fParent <> '') and Assigned(List) then begin
  755. st.LinesBefore.Add(Format(' %-62s = %s;', [ fName, List.GetPascalName(fParent, 0) ]));
  756. st.LinesBefore.Add(Format(' P%-61s = ^%0:s;', [ fName ]));
  757. st.LinesBefore.Add(Format(' PP%-60s = ^P%0:s;', [ fName ]));
  758. st.LinesBefore.Add('');
  759. end;
  760. end;
  761. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  762. //THeaderGenerator.TvkType//////////////////////////////////////////////////////////////////////////////////////////////
  763. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  764. procedure THeaderGenerator.TvkType.ReadXml(const xml: TDOMNode);
  765. begin
  766. inherited ReadXml(xml);
  767. Name := GetNodeValue(xml, 'name');
  768. Parent := GetNodeValue(xml, 'type');
  769. end;
  770. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  771. constructor THeaderGenerator.TvkType.Create;
  772. begin
  773. inherited Create(otType);
  774. end;
  775. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  776. //THeaderGenerator.TvkHandle////////////////////////////////////////////////////////////////////////////////////////////
  777. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  778. constructor THeaderGenerator.TvkHandle.Create;
  779. begin
  780. inherited Create(otHandle);
  781. end;
  782. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  783. procedure THeaderGenerator.TvkHandle.ReadXml(const xml: TDOMNode);
  784. begin
  785. inherited ReadXml(xml);
  786. Name := GetNodeValue (xml, 'name');
  787. //Parent := GetNodeAttrib(xml, 'parent', 'VkHandle');
  788. Parent := 'VkHandle';
  789. end;
  790. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  791. function THeaderGenerator.TvkFunctionBase.GetDependencyOrder: Integer;
  792. var a: TArgument;
  793. begin
  794. result := inherited GetDependencyOrder;
  795. if not Assigned(List) then
  796. exit;
  797. for a in fArguments do
  798. result := max(result, List.IndexOfObj(a.Typ, VK_OBJECT_TYPES));
  799. end;
  800. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  801. procedure THeaderGenerator.TvkFunctionBase.GenPascalName(var aName: WideString; const aPointer: Integer;
  802. const aArrSize: WideString);
  803. var rx: TRegExpr;
  804. begin
  805. if (aPointer > 0) then
  806. Exception.Create('function pointer can not be used as pointer type');
  807. if (aArrSize <> '') then
  808. Exception.Create('function pointer can not be used as array type');
  809. rx := TRegExpr.Create('PFN_([-_A-Za-z0-9]+)');
  810. try
  811. if rx.Exec(String(fName))
  812. then aName := 'T' + WideString(rx.Match[1])
  813. else aName := 'T' + fName;
  814. finally
  815. FreeAndNil(rx);
  816. end;
  817. end;
  818. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  819. procedure THeaderGenerator.TvkFunctionBase.GenerateFunctionPtr(const aCode, aSub: TStringTree);
  820. var
  821. st: TStringTree;
  822. i: Integer;
  823. s: WideString;
  824. a: TArgument;
  825. begin
  826. if Assigned(fList) then begin
  827. fList.GenerateCode(fReturnValue.Typ, aCode);
  828. for a in fArguments do
  829. fList.GenerateCode(a.Typ, aCode);
  830. end;
  831. st := aSub.GetOrCreateItem(GetPascalName(0, ''));
  832. if (st.LinesBefore.Count = 0) then begin
  833. // head
  834. s := WideString(Format(' %s', [ GetPascalName(0, '') ]));
  835. if (ReturnValue.Typ = 'void') and (ReturnValue.IsPointer = 0)
  836. then s := s + ' = procedure('
  837. else s := s + ' = function (';
  838. st.LinesBefore.Add(s);
  839. // params
  840. for i := 0 to fArguments.Count-1 do begin
  841. s := ' ';
  842. a := fArguments[i];
  843. if a.IsConst
  844. then s := s + 'const '
  845. else s := s + ' ';
  846. s := s + Format('%-30s%s', [ PrepareName(a.Name, a.Typ)+':', List.GetPascalName(a.Typ, a.IsPointer) ]);
  847. if (i < fArguments.Count-1) then
  848. s := s + '; ';
  849. st.LinesBefore.Add(s);
  850. end;
  851. // result
  852. s := ' )';
  853. if (ReturnValue.Typ <> 'void') or (ReturnValue.IsPointer > 0) then
  854. s := s + ': ' + List.GetPascalName(ReturnValue.Typ, ReturnValue.IsPointer);
  855. s := s + '; {$IFDEF VK_CDECL}cdecl{$ELSE}stdcall{$ENDIF};';
  856. st.LinesBefore.Add(s);
  857. st.LinesBefore.Add('');
  858. end;
  859. end;
  860. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  861. constructor THeaderGenerator.TvkFunctionBase.Create(const aType: TvkObjectType);
  862. begin
  863. inherited Create(aType);
  864. fReturnValue := TArgument.Create;
  865. fArguments := TArgumentList.Create;
  866. end;
  867. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  868. destructor THeaderGenerator.TvkFunctionBase.Destroy;
  869. begin
  870. FreeAndNil(fReturnValue);
  871. FreeAndNil(fArguments);
  872. inherited Destroy;
  873. end;
  874. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  875. //THeaderGenerator.TvkFunctionPtr///////////////////////////////////////////////////////////////////////////////////
  876. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  877. procedure THeaderGenerator.TvkFunctionPtr.ReadXml(const xml: TDOMNode);
  878. var
  879. x, p: TDOMNode;
  880. a: TArgument;
  881. rx: TRegExpr;
  882. begin
  883. x := xml.FindNode('name');
  884. if not Assigned(x) then
  885. raise XmlException.Create(xml.NodeName + ' witout child ''name''', xml);
  886. Name := x.TextContent;
  887. p := x.PreviousSibling;
  888. if not Assigned(p) then
  889. raise XmlException.Create(xml.NodeName + ' witout return type', xml);
  890. rx := TRegExpr.Create('typedef\s+((const)?)\s*([-_A-Za-z0-9\s]+?)\s*((\*\s*(const)?\s*)*)\s*\(');
  891. try
  892. if not rx.Exec(String(p.TextContent)) then
  893. raise XmlException.Create('unable to parse return type of function pointer', xml);
  894. ReturnValue.IsConst := (Trim(rx.Match[1]) = 'const');
  895. ReturnValue.Typ := WideString(Trim(rx.Match[3]));
  896. ReturnValue.IsPointer := PointerCount(Trim(rx.Match[4]));
  897. x := xml.FirstChild;
  898. while Assigned(x) do begin
  899. if (x.NodeName = 'type') then begin
  900. a := TArgument.Create;
  901. try
  902. p := x.PreviousSibling;
  903. if not Assigned(p) then
  904. raise XmlException.Create('unable to parse function pointer arguments', xml);
  905. rx.Expression := '[A-Za-z]*';
  906. a.IsConst := rx.Exec(String(p.TextContent)) and (rx.Match[0] = 'const');
  907. a.Typ := x.TextContent;
  908. p := x.NextSibling;
  909. if not Assigned(p) then
  910. raise XmlException.Create('unable to parse function pointer arguments', xml);
  911. rx.Expression := '((\*\s*(const)?\s*)*)\s*([-_A-Za-z0-9]+)';
  912. if not rx.Exec(String(p.TextContent)) then
  913. raise XmlException.Create('unable to parse function pointer arguments', xml);
  914. a.IsPointer := PointerCount((rx.Match[1]));
  915. a.Name := WideString(Trim(rx.Match[4]));
  916. Arguments.Add(a);
  917. except
  918. FreeAndNil(a)
  919. end;
  920. end;
  921. x := x.NextSibling;
  922. end;
  923. finally
  924. FreeAndNil(rx);
  925. end;
  926. end;
  927. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  928. procedure THeaderGenerator.TvkFunctionPtr.GenerateCode(const aCode: TStringTree);
  929. var st: TStringTree;
  930. begin
  931. inherited GenerateCode(aCode);
  932. st := aCode.GetOrCreateItem(KEYWORD_INTF).GetOrCreateItem(KEYWORD_FUNCPTR);
  933. if (st.LinesBefore.Count = 0) then
  934. st.LinesBefore.Add('type { function pointers }');
  935. GenerateFunctionPtr(aCode, st);
  936. end;
  937. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  938. constructor THeaderGenerator.TvkFunctionPtr.Create;
  939. begin
  940. inherited Create(otFuncPtr);
  941. end;
  942. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  943. //THeaderGenerator.TvkCommand///////////////////////////////////////////////////////////////////////////////////////
  944. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  945. procedure THeaderGenerator.TvkCommand.ReadXml(const xml: TDOMNode);
  946. procedure ParseProto(const x: TDOMNode);
  947. begin
  948. Name := GetNodeValue(x, 'name');
  949. ReturnValue.Typ := GetNodeValue(x, 'type');
  950. end;
  951. procedure ParseParam(const x: TDOMNode);
  952. var
  953. p: TDOMNode;
  954. a: TArgument;
  955. rx: TRegExpr;
  956. begin
  957. a := TArgument.Create;
  958. rx := TRegExpr.Create;
  959. try try
  960. p := x.FindNode('name');
  961. if not Assigned(p) then
  962. raise XmlException.Create('unable to parse command; param without name', xml);
  963. a.Name := p.TextContent;
  964. p := x.FindNode('type');
  965. if not Assigned(p) then
  966. raise XmlException.Create('unable to parse command; param without type', xml);
  967. a.Typ := p.TextContent;
  968. a.IsConst := Assigned(p.PreviousSibling) and (Trim(p.PreviousSibling.TextContent) = 'const');
  969. rx.Expression := '((\*\s*(const)?\s*)*)';
  970. if Assigned(p.NextSibling) and rx.Exec(String(p.NextSibling.TextContent)) then
  971. a.IsPointer := PointerCount(rx.Match[0]);
  972. Arguments.Add(a);
  973. finally
  974. FreeAndNil(rx);
  975. end;
  976. except
  977. FreeAndNil(a);
  978. raise;
  979. end;
  980. end;
  981. var
  982. x: TDOMNode;
  983. begin
  984. inherited ReadXml(xml);
  985. x := xml.FirstChild;
  986. while Assigned(x) do begin
  987. if (x.NodeName = 'proto') then
  988. ParseProto(x)
  989. else if (x.NodeName = 'param') then
  990. ParseParam(x);
  991. x := x.NextSibling;
  992. end;
  993. end;
  994. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  995. procedure THeaderGenerator.TvkCommand.GenerateCode(const aCode: TStringTree);
  996. var st: TStringTree;
  997. begin
  998. inherited GenerateCode(aCode);
  999. st := aCode.GetOrCreateItem(KEYWORD_INTF).GetOrCreateItem(KEYWORD_COMMAND);
  1000. if (st.LinesBefore.Count = 0) then
  1001. st.LinesBefore.Add('type { commands }');
  1002. GenerateFunctionPtr(aCode, st);
  1003. st := aCode.GetOrCreateItem(KEYWORD_INTF).GetOrCreateItem(KEYWORD_VAR);
  1004. if (st.LinesBefore.Count = 0) then
  1005. st.LinesBefore.Add('var');
  1006. st := st.GetOrCreateItem(fName);
  1007. if (st.LinesBefore.Count = 0) then
  1008. st.LinesBefore.Add(Format(' %-50s%s;', [fName + ':', GetPascalName(0, '')]));
  1009. st := aCode.GetOrCreateItem(KEYWORD_IMPL).GetOrCreateItem(KEYWORD_CODE).GetOrCreateItem(fName);
  1010. if (st.LinesBefore.Count = 0) then
  1011. st.LinesBefore.Add(Format(' %-50s := T%0:-50s(vkGetProcAddress(%-50saInstance));', [fName, '''' + fName + ''',']));
  1012. end;
  1013. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1014. constructor THeaderGenerator.TvkCommand.Create;
  1015. begin
  1016. inherited Create(otCommand);
  1017. end;
  1018. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1019. //THeaderGenerator.TvkStruct////////////////////////////////////////////////////////////////////////////////////////
  1020. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1021. function THeaderGenerator.TvkStruct.GetDependencyOrder: Integer;
  1022. var
  1023. m: TMember;
  1024. begin
  1025. result := inherited GetDependencyOrder;
  1026. if not Assigned(List) then
  1027. exit;
  1028. for m in fMember do
  1029. result := max(result, List.IndexOfObj(m.Typ, VK_OBJECT_TYPES) + 1);
  1030. end;
  1031. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1032. procedure THeaderGenerator.TvkStruct.GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString);
  1033. var i: Integer;
  1034. begin
  1035. if (aPointer > 0) then begin
  1036. aName := fName;
  1037. for i := 0 to aPointer-1 do
  1038. aName := 'P' + aName;
  1039. end else
  1040. aName := 'T' + fName;
  1041. inherited GenPascalName(aName, aPointer, aArrSize);
  1042. end;
  1043. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1044. procedure THeaderGenerator.TvkStruct.ReadXml(const xml: TDOMNode);
  1045. var
  1046. x, p: TDOMNode;
  1047. m: TMember;
  1048. rx: TRegExpr;
  1049. begin
  1050. Name := GetNodeAttrib(xml, 'name');
  1051. IsUnion := (GetNodeAttrib(xml, 'category') = 'union');
  1052. x := xml.FirstChild;
  1053. while Assigned(x) do begin
  1054. if (x.NodeName = 'member') then begin
  1055. m := TMember.Create;
  1056. rx := TRegExpr.Create('');
  1057. try try
  1058. // read name
  1059. p := x.FindNode('name');
  1060. if not Assigned(p) then
  1061. raise XmlException.Create('struct member without name', xml);
  1062. m.Name := p.TextContent;
  1063. p := p.NextSibling;
  1064. while Assigned(p) do begin
  1065. m.Name := m.Name + p.TextContent;
  1066. p := p.NextSibling;
  1067. end;
  1068. m.Name := Trim(m.Name);
  1069. rx.Expression := '([-_A-Za-z0-9]+)\s*\[\s*([-_A-Za-z0-9]+)\s*\]';
  1070. if rx.Exec(String(m.Name)) then begin
  1071. m.Name := WideString(rx.Match[1]);
  1072. m.ArrSize := WideString(rx.Match[2]);
  1073. end;
  1074. // read type
  1075. p := x.FindNode('type');
  1076. if not Assigned(p) then
  1077. raise XmlException.Create('struct member without type', xml);
  1078. m.Typ := p.TextContent;
  1079. m.IsConst := Assigned(p.PreviousSibling) and (Trim(p.PreviousSibling.TextContent) = 'const');
  1080. rx.Expression := '((\*\s*(const)?\s*)*)';
  1081. if Assigned(p.NextSibling) and rx.Exec(String(p.NextSibling.TextContent)) then
  1082. m.IsPointer := PointerCount(rx.Match[0]);
  1083. Member.Add(m);
  1084. finally
  1085. FreeAndNil(rx);
  1086. end;
  1087. except
  1088. FreeAndNil(m);
  1089. end;
  1090. end;
  1091. x := x.NextSibling;
  1092. end;
  1093. end;
  1094. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1095. procedure THeaderGenerator.TvkStruct.GenerateCode(const aCode: TStringTree);
  1096. var
  1097. m: TMember;
  1098. st: TStringTree;
  1099. postfix, prefix: String;
  1100. i, indent: Integer;
  1101. begin
  1102. inherited GenerateCode(aCode);
  1103. if Assigned(fList) then begin
  1104. for m in fMember do begin
  1105. if (m.ArrSize <> '') then
  1106. fList.GenerateCode(m.ArrSize, aCode);
  1107. fList.GenerateCode(m.Typ, aCode);
  1108. end;
  1109. end;
  1110. st := aCode.GetOrCreateItem(KEYWORD_INTF).GetOrCreateItem(KEYWORD_STRUCT);
  1111. if (st.LinesBefore.Count = 0) then
  1112. st.LinesBefore.Add('type { structures }');
  1113. st := st.GetOrCreateItem(GetPascalName(0, ''));
  1114. if (st.LinesBefore.Count = 0) then begin
  1115. postfix := ';';
  1116. indent := 50;
  1117. st.LinesBefore.Add(' T%s = record', [ fName ]);
  1118. if IsUnion then begin
  1119. st.LinesBefore.Add(' case Integer of');
  1120. postfix := ');';
  1121. indent := 42;
  1122. end;
  1123. for i := 0 to fMember.Count-1 do begin
  1124. m := fMember[i];
  1125. if IsUnion
  1126. then prefix := Format(' %.3d: (', [ i ])
  1127. else prefix := ' ';
  1128. st.LinesBefore.Add(Format('%s%-*s%s%s', [ prefix, indent, PrepareName(m.Name, m.Typ) + ': ', List.GetPascalName(m.Typ, m.IsPointer, m.ArrSize), postfix ]));
  1129. end;
  1130. st.LinesAfter.Add(' end;');
  1131. st.LinesAfter.Add(' P%s = ^T%0:s;', [ fName ]);
  1132. st.LinesAfter.Add(' PP%s = ^P%0:s;', [ fName ]);
  1133. st.LinesAfter.Add('');
  1134. end;
  1135. end;
  1136. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1137. constructor THeaderGenerator.TvkStruct.Create;
  1138. begin
  1139. inherited Create(otStruct);
  1140. fMember := TMemberList.Create;
  1141. end;
  1142. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1143. destructor THeaderGenerator.TvkStruct.Destroy;
  1144. begin
  1145. FreeAndNil(fMember);
  1146. inherited Destroy;
  1147. end;
  1148. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1149. //THeaderGenerator.TvkEnum//////////////////////////////////////////////////////////////////////////////////////////
  1150. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1151. procedure THeaderGenerator.TvkEnum.GenPascalName(var aName: WideString; const aPointer: Integer; const aArrSize: WideString);
  1152. var i: Integer;
  1153. begin
  1154. if (fName = '') then
  1155. raise Exception.Create('enum with empty name');
  1156. if (aPointer > 0) then begin
  1157. aName := fName;
  1158. for i := 0 to aPointer-1 do
  1159. aName := 'P' + aName;
  1160. end else
  1161. aName := 'T' + fName;
  1162. inherited GenPascalName(aName, aPointer, aArrSize);
  1163. end;
  1164. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1165. procedure THeaderGenerator.TvkEnum.ReadXml(const xml: TDOMNode);
  1166. var
  1167. x, p: TDOMNode;
  1168. v: TValue;
  1169. begin
  1170. Name := GetNodeAttrib(xml, 'name');
  1171. Expands := GetNodeAttrib(xml, 'expand', '');
  1172. x := xml.FirstChild;
  1173. while Assigned(x) do begin
  1174. if (x.NodeName = 'enum') then begin
  1175. v := TValue.Create;
  1176. try
  1177. v.Name := GetNodeAttrib(x, 'name');
  1178. p := x.Attributes.GetNamedItem('value');
  1179. if Assigned(p) then begin
  1180. v.IsBit := false;
  1181. v.Value := StrToValue(String(p.TextContent));
  1182. end else begin
  1183. p := x.Attributes.GetNamedItem('bitpos');
  1184. if not Assigned(p) then
  1185. raise XmlException.Create('enum without value or bitpos', xml);
  1186. v.IsBit := true;
  1187. v.Value := StrToValue(String(p.TextContent));
  1188. end;
  1189. if (v.Value = Unassigned) then
  1190. raise XmlException.Create(WideString('invalid enum value (' + p.TextContent + ')'), xml);
  1191. Values.Add(v);
  1192. except
  1193. FreeAndNil(v);
  1194. raise;
  1195. end;
  1196. end;
  1197. x := x.NextSibling;
  1198. end;
  1199. end;
  1200. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1201. procedure THeaderGenerator.TvkEnum.GenerateCode(const aCode: TStringTree);
  1202. var
  1203. st, vt: TStringTree;
  1204. i: Integer;
  1205. v: TValue;
  1206. postfix: String;
  1207. begin
  1208. inherited GenerateCode(aCode);
  1209. st := aCode
  1210. .GetOrCreateItem(KEYWORD_INTF)
  1211. .GetOrCreateItem(KEYWORD_ENUM);
  1212. if (st.LinesBefore.Count = 0) then
  1213. st.LinesBefore.Add('type { enumerations }');
  1214. st := st.GetOrCreateItem(fName);
  1215. if (st.LinesBefore.Count = 0) then begin
  1216. st.LinesBefore.Add(Format(' %s = (', [GetPascalName(0, '')]));
  1217. postfix := ',';
  1218. vt := st.GetOrCreateItem(KEYWORD_ENUM_VALUES);
  1219. for i := 0 to fValues.Count-1 do begin
  1220. v := fValues[i];
  1221. if (i = fValues.Count-1) and (fExpands = '') then
  1222. postfix := '';
  1223. if (v.IsBit)
  1224. then vt.LinesBefore.Add(Format(' %-60s = (1 shl %s)%s', [v.Name, v.Value, postfix]))
  1225. else vt.LinesBefore.Add(Format(' %-60s = %s%s', [v.Name, v.Value, postfix]));
  1226. end;
  1227. if (fExpands <> '') then
  1228. st.LinesAfter.Add(Format(' %-60s = %s', [fExpands + '_MAX_ENUM', '$7FFFFFFF']));
  1229. st.LinesAfter.Add(' );');
  1230. st.LinesAfter.Add(' P%s = ^T%0:s;', [ fName ]);
  1231. st.LinesAfter.Add(' PP%s = ^P%0:s;', [ fName ]);
  1232. st.LinesAfter.Add('');
  1233. end;
  1234. end;
  1235. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1236. constructor THeaderGenerator.TvkEnum.Create;
  1237. begin
  1238. inherited Create(otEnum);
  1239. fValues := TValueList.Create;
  1240. end;
  1241. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1242. destructor THeaderGenerator.TvkEnum.Destroy;
  1243. begin
  1244. FreeAndNil(fValues);
  1245. inherited Destroy;
  1246. end;
  1247. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1248. //THeaderGenerator.TvkConst/////////////////////////////////////////////////////////////////////////////////////////////
  1249. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1250. procedure THeaderGenerator.TvkConst.GenerateCode(const aCode: TStringTree);
  1251. var
  1252. st: TStringTree;
  1253. begin
  1254. inherited GenerateCode(aCode);
  1255. st := aCode
  1256. .GetOrCreateItem(KEYWORD_INTF)
  1257. .GetOrCreateItem(KEYWORD_CONST);
  1258. if (st.LinesBefore.Count = 0) then
  1259. st.LinesBefore.Add('const');
  1260. st := st.GetOrCreateItem(fName);
  1261. if (st.LinesBefore.Count = 0) then
  1262. st.LinesBefore.Add(Format(' %-62s = %s;', [fName, fValue]));
  1263. end;
  1264. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1265. constructor THeaderGenerator.TvkConst.Create;
  1266. begin
  1267. inherited Create(otConst);
  1268. end;
  1269. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1270. //THeaderGenerator//////////////////////////////////////////////////////////////////////////////////////////////////////
  1271. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1272. procedure THeaderGenerator.GenerateCode(const aName: WideString; const aTemplate: String; const aOutput: String);
  1273. type
  1274. TPosMap = specialize TutlMap<string, Integer>;
  1275. var
  1276. CurrentName: WideString;
  1277. CurrentNumber: Integer;
  1278. Code: TStringTree;
  1279. PosMap: TPosMap;
  1280. procedure UpdateComment(const aRoot: TStringTree; const aName: String);
  1281. var
  1282. i: Integer;
  1283. s, tmp: String;
  1284. st: TStringTree;
  1285. begin
  1286. st := aRoot.GetOrCreateItem(aName);
  1287. if PosMap.TryGetValue(aName, i) and (st.ItemCount > i) and (CurrentName <> '') then
  1288. with st.CreateItemAt(aName + CurrentName, i).LinesBefore do begin
  1289. s := ' ' + CurrentName + ' ';
  1290. if ((Length(s) and 1) = 1) then
  1291. s := s + '=';
  1292. tmp := StringOfChar('=', (100 - Length(s)) div 2);
  1293. s := '{ ' + tmp + s + tmp + ' }';
  1294. Add('');
  1295. Add(s);
  1296. end;
  1297. PosMap[aName] := st.ItemCount;
  1298. end;
  1299. procedure UpdateComments;
  1300. var st: TStringTree;
  1301. begin
  1302. st := Code.GetOrCreateItem(KEYWORD_INTF);
  1303. UpdateComment(st, KEYWORD_CONST);
  1304. UpdateComment(st, KEYWORD_TYPES);
  1305. UpdateComment(st, KEYWORD_ENUM);
  1306. UpdateComment(st, KEYWORD_FUNCPTR);
  1307. UpdateComment(st, KEYWORD_STRUCT);
  1308. UpdateComment(st, KEYWORD_COMMAND);
  1309. UpdateComment(st, KEYWORD_VAR);
  1310. st := Code.GetOrCreateItem(KEYWORD_IMPL);
  1311. UpdateComment(st, KEYWORD_CODE);
  1312. end;
  1313. procedure AddGuard(const aTree: TStringTree; const aName: String);
  1314. var
  1315. i: Integer;
  1316. begin
  1317. for i := 0 to aTree.ItemCount-1 do
  1318. AddGuard(aTree.ItemsByIndex[i], aName);
  1319. with aTree.GetOrCreateItem('GUARD' + aName).LinesBefore do begin
  1320. Add('{$IFDEF ' + aName + '}');
  1321. end;
  1322. end;
  1323. procedure RemoveGuard(const aTree: TStringTree; const aName: String);
  1324. var
  1325. i: Integer;
  1326. g: TStringTree;
  1327. begin
  1328. for i := 0 to aTree.ItemCount-1 do
  1329. RemoveGuard(aTree.ItemsByIndex[i], aName);
  1330. g := aTree.GetOrCreateItem('GUARD' + aName);
  1331. if (g = aTree.ItemsByIndex[aTree.ItemCount-1])
  1332. then aTree.DestroyItem('GUARD' + aName)
  1333. else with aTree.GetOrCreateItem('GUARD_END'+ aName).LinesAfter do begin
  1334. Add('{$ENDIF}');
  1335. Add('');
  1336. end;
  1337. end;
  1338. procedure CreateGuard(const aName: String);
  1339. begin
  1340. if (aName = '') then
  1341. exit;
  1342. AddGuard(Code, aName);
  1343. end;
  1344. procedure CloseGuard(const aName: String);
  1345. begin
  1346. if (aName = '') then
  1347. exit;
  1348. RemoveGuard(Code, aName);
  1349. end;
  1350. procedure GenerateFeatureCode(const aName: String);
  1351. var
  1352. i: Integer;
  1353. begin
  1354. i := fObjects.IndexOfObj(aName, VK_OBJECT_ALL);
  1355. if (i >= 0) then
  1356. fObjects[i].GenerateCode(Code);
  1357. end;
  1358. function GenerateEnum(const xml: TDOMNode): Boolean;
  1359. var
  1360. name: DOMString;
  1361. ex: DOMString;
  1362. value: DOMString;
  1363. offset: Integer;
  1364. s: String;
  1365. c: TvkConst;
  1366. begin
  1367. result := false;
  1368. name := GetNodeAttrib(xml, 'name');
  1369. if TryGetNodeAttrib(xml, 'extends', ex) then begin
  1370. if (CurrentNumber <= 0) then
  1371. raise Exception.Create('current extension has no number');
  1372. offset := StrToInt(GetNodeAttrib(xml, 'offset'));
  1373. offset := 1000000000 + 1000 * (CurrentNumber-1) + offset;
  1374. if (GetNodeAttrib(xml, 'dir', '') = '-') then
  1375. offset := -1 * offset;
  1376. Code
  1377. .GetOrCreateItem(KEYWORD_INTF)
  1378. .GetOrCreateItem(KEYWORD_ENUM)
  1379. .GetOrCreateItem(ex)
  1380. .GetOrCreateItem(KEYWORD_ENUM_VALUES)
  1381. .LinesBefore.Add(Format(' %-60s = %s,', [name, IntToStr(offset)]));
  1382. end else begin
  1383. if not TryGetNodeAttrib(xml, 'value', value) then
  1384. exit;
  1385. s := StringReplace(string(value), '"', '''', [rfReplaceAll]);
  1386. c := TvkConst.Create;
  1387. try
  1388. c.Name := name;
  1389. c.Value := s;
  1390. fObjects.AddObject(c);
  1391. except
  1392. FreeAndNil(c);
  1393. raise;
  1394. end;
  1395. c.GenerateCode(Code);
  1396. end;
  1397. result := true;
  1398. end;
  1399. procedure GenerateFeatureCode(const xml: TDOMNode);
  1400. var
  1401. x: TDOMNode;
  1402. begin
  1403. x := xml.FirstChild;
  1404. while Assigned(x) do begin
  1405. if (x.NodeName = 'type') or
  1406. (x.NodeName = 'command') then
  1407. begin
  1408. GenerateFeatureCode(GetNodeAttrib(x, 'name'));
  1409. end else if (x.NodeName = 'enum') then begin
  1410. if not GenerateEnum(x) then
  1411. GenerateFeatureCode(GetNodeAttrib(x, 'name'));
  1412. end;
  1413. x := x.NextSibling;
  1414. end;
  1415. end;
  1416. procedure Generate(const xml: TDOMNode);
  1417. var
  1418. x: TDOMNode;
  1419. g: DOMString;
  1420. begin
  1421. UpdateComments;
  1422. g := GetNodeAttrib(xml, 'protect', '');
  1423. CreateGuard(g);
  1424. try
  1425. CurrentName := GetNodeAttrib(xml, 'name');
  1426. if not TryStrToInt(String(GetNodeAttrib(xml, 'number', '-1')), CurrentNumber) then
  1427. CurrentNumber := -1;
  1428. WriteLn('generate code for feature: ', CurrentName);
  1429. x := xml.FirstChild;
  1430. while Assigned(x) do begin
  1431. if (x.NodeName = 'require') then
  1432. GenerateFeatureCode(x);
  1433. x := x.NextSibling;
  1434. end;
  1435. finally
  1436. CloseGuard(g);
  1437. UpdateComments;
  1438. CurrentName := '';
  1439. end;
  1440. end;
  1441. procedure EnableFeature;
  1442. var x: TDOMNode;
  1443. begin
  1444. GenerateFeatureCode('VK_API_VERSION');
  1445. GenerateFeatureCode('VK_API_VERSION_MAJOR');
  1446. GenerateFeatureCode('VK_API_VERSION_MINOR');
  1447. GenerateFeatureCode('VK_API_VERSION_BUGFIX');
  1448. x := fDocument.DocumentElement.FirstChild;
  1449. while Assigned(x) do begin
  1450. if (x.NodeName = 'feature') and (GetNodeAttrib(x, 'api', '') = aName) then
  1451. Generate(x);
  1452. x := x.NextSibling;
  1453. end;
  1454. end;
  1455. procedure EnableExtensions;
  1456. var
  1457. x: TDOMNode;
  1458. g: String;
  1459. begin
  1460. x := fDocument.DocumentElement.FindNode('extensions');
  1461. if not Assigned(x) then
  1462. exit;
  1463. x := x.FirstChild;
  1464. while Assigned(x) do begin
  1465. if (x.NodeName = 'extension') and (GetNodeAttrib(x, 'supported', '') = aName) then
  1466. Generate(x);
  1467. x := x.NextSibling;
  1468. end;
  1469. end;
  1470. procedure RemoveEmptyLines(const aText: TStrings);
  1471. var
  1472. i: Integer;
  1473. b: Boolean;
  1474. rx: TRegExpr;
  1475. begin
  1476. i := 0;
  1477. rx := TRegExpr.Create('{%.*?%}');
  1478. try
  1479. while (i < aText.Count) do begin
  1480. if (rx.Exec(aText[i])) then begin
  1481. aText.Delete(i);
  1482. continue;
  1483. end;
  1484. if (Trim(aText[i]) = '') then begin
  1485. if b then begin
  1486. aText.Delete(i);
  1487. continue;
  1488. end else
  1489. b := true;
  1490. end else
  1491. b := false;
  1492. inc(i);
  1493. end;
  1494. finally
  1495. FreeAndNil(rx);
  1496. end;
  1497. end;
  1498. procedure PrepareTemplate;
  1499. var
  1500. sl: TStringList;
  1501. s, n: String;
  1502. st: TStringTree;
  1503. p0, p1: Integer;
  1504. begin
  1505. sl := TStringList.Create;
  1506. try
  1507. sl.LoadFromFile(aTemplate);
  1508. s := sl.Text;
  1509. p0 := Pos('{%', s);
  1510. while (p0 > 0) do begin
  1511. p1 := Pos('%}', s, p0);
  1512. if (p1 <= 0) then
  1513. raise Exception.Create('unclosed tag in template');
  1514. n := Trim(Copy(s, p0+2, p1-p0-2));
  1515. st := Code.Items[n];
  1516. n := Copy(s, p0, p1 - p0 + 2);
  1517. if Assigned(st)
  1518. then s := StringReplace(s, n, st.Text, [rfReplaceAll])
  1519. else s := StringReplace(s, n, '', [rfReplaceAll]);
  1520. p0 := Pos('{%', s, p0);
  1521. end;
  1522. sl.Text := Trim(s);
  1523. RemoveEmptyLines(sl);
  1524. sl.SaveToFile(aOutput);
  1525. finally
  1526. FreeAndNil(sl);
  1527. end;
  1528. end;
  1529. begin
  1530. if not Assigned(fDocument) then
  1531. raise Exception.Create('no xml file loaded, did you call ReadFile before?');
  1532. Code := TStringTree.Create;
  1533. PosMap := TPosMap.Create;
  1534. try
  1535. Code.GetOrCreateItem(KEYWORD_COMMENT).LinesBefore.Text := Trim(String(fComment));
  1536. with Code.GetOrCreateItem(KEYWORD_INTF) do begin
  1537. GetOrCreateItem(KEYWORD_CONST).LinesAfter.Add('');
  1538. GetOrCreateItem(KEYWORD_TYPES).LinesAfter.Add('');
  1539. GetOrCreateItem(KEYWORD_ENUM).LinesAfter.Add('');
  1540. GetOrCreateItem(KEYWORD_FUNCPTR).LinesAfter.Add('');
  1541. GetOrCreateItem(KEYWORD_STRUCT).LinesAfter.Add('');
  1542. GetOrCreateItem(KEYWORD_COMMAND).LinesAfter.Add('');
  1543. GetOrCreateItem(KEYWORD_VAR).LinesAfter.Add('');
  1544. end;
  1545. with Code.GetOrCreateItem(KEYWORD_IMPL) do begin
  1546. GetOrCreateItem(KEYWORD_CODE).LinesAfter.Add('');
  1547. end;
  1548. Code.GetOrCreateItem(KEYWORD_IMPL);
  1549. EnableFeature;
  1550. EnableExtensions;
  1551. PrepareTemplate;
  1552. finally
  1553. FreeAndNil(PosMap);
  1554. FreeAndNil(Code);
  1555. end;
  1556. end;
  1557. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1558. procedure THeaderGenerator.ReadFile(const aFilename: String);
  1559. var
  1560. fs: TFileStream;
  1561. begin
  1562. if not FileExists(aFilename) then
  1563. raise Exception.Create('file does not exist: ' + aFilename);
  1564. fs := TFileStream.Create(aFilename, fmOpenRead);
  1565. try
  1566. ReadFile(fs);
  1567. finally
  1568. FreeAndNil(fs);
  1569. end;
  1570. end;
  1571. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1572. procedure THeaderGenerator.ReadFile(const aStream: TStream);
  1573. var
  1574. xml: TDOMNode;
  1575. procedure ParseTypes(const xml: TDOMNode);
  1576. var
  1577. x: TDOMNode;
  1578. begin
  1579. x := xml.FirstChild;
  1580. while Assigned(x) do begin
  1581. TvkObject.CreateFromXml(x, fObjects);
  1582. x := x.NextSibling;
  1583. end;
  1584. end;
  1585. begin
  1586. Clear;
  1587. ReadXMLFile(fDocument, aStream);
  1588. WriteLn('read xml data');
  1589. xml := fDocument.DocumentElement.FirstChild;
  1590. while Assigned(xml) do begin
  1591. if (xml.NodeName = 'types') or (xml.NodeName = 'commands') then
  1592. ParseTypes(xml)
  1593. else if (xml.NodeName = 'enums') then
  1594. TvkObject.CreateFromXml(xml, fObjects)
  1595. else if (xml.NodeName = 'comment') then
  1596. fComment := WideString(Trim(String(xml.TextContent)));
  1597. xml := xml.NextSibling;
  1598. end;
  1599. end;
  1600. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1601. procedure THeaderGenerator.Clear;
  1602. procedure CreateBaseType(const aName, aAlias, aParent: DOMString);
  1603. var t: TvkTypeBase;
  1604. begin
  1605. t := TvkTypeBase.Create(otType);
  1606. try
  1607. t.Name := aName;
  1608. t.Alias := aAlias;
  1609. t.Parent := aParent;
  1610. t.List := fObjects;
  1611. fObjects.Add(t);
  1612. except
  1613. FreeAndNil(t);
  1614. raise;
  1615. end;
  1616. end;
  1617. begin
  1618. fObjects.Clear;
  1619. CreateBaseType('void', 'VkVoid', '');
  1620. CreateBaseType('uint8_t', 'VkUint8', '');
  1621. CreateBaseType('uint16_t', 'VkUint16', '');
  1622. CreateBaseType('uint32_t', 'VkUint32', '');
  1623. CreateBaseType('uint64_t', 'VkUint64', '');
  1624. CreateBaseType('int16_t', 'VkInt16', '');
  1625. CreateBaseType('int32_t', 'VkInt32', '');
  1626. CreateBaseType('int64_t', 'VkInt64', '');
  1627. CreateBaseType('float', 'VkFloat', '');
  1628. CreateBaseType('double', 'VkDouble', '');
  1629. CreateBaseType('char', 'VkChar', '');
  1630. CreateBaseType('size_t', 'VkSize', '');
  1631. end;
  1632. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1633. constructor THeaderGenerator.Create;
  1634. begin
  1635. inherited Create;
  1636. fObjects := TvkObjectList.Create;
  1637. end;
  1638. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1639. destructor THeaderGenerator.Destroy;
  1640. begin
  1641. FreeAndNil(fObjects);
  1642. FreeAndNil(fDocument);
  1643. inherited Destroy;
  1644. end;
  1645. end.