paswrite.pp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. {
  2. This file is part of the Free Component Library
  3. Pascal tree source file writer
  4. Copyright (c) 2003 by
  5. Areca Systems GmbH / Sebastian Guenther, [email protected]
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$mode objfpc}
  13. {$h+}
  14. unit PasWrite;
  15. interface
  16. uses Classes, PasTree;
  17. type
  18. TPasWriter = class
  19. private
  20. FStream: TStream;
  21. IsStartOfLine: Boolean;
  22. Indent, CurDeclSection: string;
  23. DeclSectionStack: TList;
  24. procedure IncIndent;
  25. procedure DecIndent;
  26. procedure IncDeclSectionLevel;
  27. procedure DecDeclSectionLevel;
  28. procedure PrepareDeclSection(const ADeclSection: string);
  29. public
  30. constructor Create(AStream: TStream);
  31. destructor Destroy; override;
  32. procedure wrt(const s: string);
  33. procedure wrtln(const s: string);overload;
  34. procedure wrtln;overload;
  35. procedure WriteElement(AElement: TPasElement);
  36. procedure WriteType(AType: TPasType);
  37. procedure WriteModule(AModule: TPasModule);
  38. procedure WriteSection(ASection: TPasSection);
  39. procedure WriteClass(AClass: TPasClassType);
  40. procedure WriteVariable(AVar: TPasVariable);
  41. procedure WriteProcDecl(AProc: TPasProcedure);
  42. procedure WriteProcImpl(AProc: TPasProcedureImpl);
  43. procedure WriteProperty(AProp: TPasProperty);
  44. procedure WriteImplBlock(ABlock: TPasImplBlock);
  45. procedure WriteImplElement(AElement: TPasImplElement;
  46. AAutoInsertBeginEnd: Boolean);
  47. procedure WriteImplCommand(ACommand: TPasImplCommand);
  48. procedure WriteImplCommands(ACommands: TPasImplCommands);
  49. procedure WriteImplIfElse(AIfElse: TPasImplIfElse);
  50. procedure WriteImplForLoop(AForLoop: TPasImplForLoop);
  51. property Stream: TStream read FStream;
  52. end;
  53. procedure WritePasFile(AElement: TPasElement; const AFilename: string);overload;
  54. procedure WritePasFile(AElement: TPasElement; AStream: TStream);overload;
  55. implementation
  56. uses SysUtils;
  57. type
  58. PDeclSectionStackElement = ^TDeclSectionStackElement;
  59. TDeclSectionStackElement = record
  60. LastDeclSection, LastIndent: string;
  61. end;
  62. constructor TPasWriter.Create(AStream: TStream);
  63. begin
  64. FStream := AStream;
  65. IsStartOfLine := True;
  66. DeclSectionStack := TList.Create;
  67. end;
  68. destructor TPasWriter.Destroy;
  69. var
  70. i: Integer;
  71. El: PDeclSectionStackElement;
  72. begin
  73. for i := 0 to DeclSectionStack.Count - 1 do
  74. begin
  75. El := PDeclSectionStackElement(DeclSectionStack[i]);
  76. Dispose(El);
  77. end;
  78. DeclSectionStack.Free;
  79. inherited Destroy;
  80. end;
  81. procedure TPasWriter.wrt(const s: string);
  82. begin
  83. if IsStartOfLine then
  84. begin
  85. if Length(Indent) > 0 then
  86. Stream.Write(Indent[1], Length(Indent));
  87. IsStartOfLine := False;
  88. end;
  89. Stream.Write(s[1], Length(s));
  90. end;
  91. const
  92. LF: string = #10;
  93. procedure TPasWriter.wrtln(const s: string);
  94. begin
  95. wrt(s);
  96. Stream.Write(LF[1], 1);
  97. IsStartOfLine := True;
  98. end;
  99. procedure TPasWriter.wrtln;
  100. begin
  101. Stream.Write(LF[1], 1);
  102. IsStartOfLine := True;
  103. end;
  104. procedure TPasWriter.WriteElement(AElement: TPasElement);
  105. begin
  106. if AElement.ClassType = TPasModule then
  107. WriteModule(TPasModule(AElement))
  108. else if AElement.ClassType = TPasSection then
  109. WriteSection(TPasSection(AElement))
  110. else if AElement.ClassType = TPasVariable then
  111. WriteVariable(TPasVariable(AElement))
  112. else if AElement.InheritsFrom(TPasType) then
  113. WriteType(TPasType(AElement))
  114. else if AElement.InheritsFrom(TPasProcedure) then
  115. WriteProcDecl(TPasProcedure(AElement))
  116. else if AElement.InheritsFrom(TPasProcedureImpl) then
  117. WriteProcImpl(TPasProcedureImpl(AElement))
  118. else if AElement.ClassType = TPasProperty then
  119. WriteProperty(TPasProperty(AElement))
  120. else
  121. raise Exception.Create('Writing not implemented for ' +
  122. AElement.ElementTypeName + ' nodes');
  123. end;
  124. procedure TPasWriter.WriteType(AType: TPasType);
  125. begin
  126. if AType.ClassType = TPasUnresolvedTypeRef then
  127. wrt(AType.Name)
  128. else if AType.ClassType = TPasClassType then
  129. WriteClass(TPasClassType(AType))
  130. else
  131. raise Exception.Create('Writing not implemented for ' +
  132. AType.ElementTypeName + ' nodes');
  133. end;
  134. procedure TPasWriter.WriteModule(AModule: TPasModule);
  135. begin
  136. wrtln('unit ' + AModule.Name + ';');
  137. wrtln;
  138. wrtln('interface');
  139. wrtln;
  140. WriteSection(AModule.InterfaceSection);
  141. Indent := '';
  142. wrtln;
  143. wrtln;
  144. wrtln('implementation');
  145. if Assigned(AModule.ImplementationSection) then
  146. begin
  147. wrtln;
  148. WriteSection(AModule.ImplementationSection);
  149. end;
  150. wrtln;
  151. wrtln('end.');
  152. end;
  153. procedure TPasWriter.WriteSection(ASection: TPasSection);
  154. var
  155. i: Integer;
  156. begin
  157. if ASection.UsesList.Count > 0 then
  158. begin
  159. wrt('uses ');
  160. for i := 0 to ASection.UsesList.Count - 1 do
  161. begin
  162. if i > 0 then
  163. wrt(', ');
  164. wrt(TPasElement(ASection.UsesList[i]).Name);
  165. end;
  166. wrtln(';');
  167. wrtln;
  168. end;
  169. CurDeclSection := '';
  170. for i := 0 to ASection.Declarations.Count - 1 do
  171. WriteElement(TPasElement(ASection.Declarations[i]));
  172. end;
  173. procedure TPasWriter.WriteClass(AClass: TPasClassType);
  174. var
  175. i: Integer;
  176. Member: TPasElement;
  177. LastVisibility, CurVisibility: TPasMemberVisibility;
  178. begin
  179. PrepareDeclSection('type');
  180. wrt(AClass.Name + ' = ');
  181. if AClass.IsPacked then
  182. wrt('packed '); // 12/04/04 - Dave - Added
  183. case AClass.ObjKind of
  184. okObject: wrt('object');
  185. okClass: wrt('class');
  186. okInterface: wrt('interface');
  187. end;
  188. if Assigned(AClass.AncestorType) then
  189. wrtln('(' + AClass.AncestorType.Name + ')')
  190. else
  191. wrtln;
  192. IncIndent;
  193. LastVisibility := visDefault;
  194. for i := 0 to AClass.Members.Count - 1 do
  195. begin
  196. Member := TPasElement(AClass.Members[i]);
  197. CurVisibility := Member.Visibility;
  198. if CurVisibility <> LastVisibility then
  199. begin
  200. DecIndent;
  201. case CurVisibility of
  202. visPrivate: wrtln('private');
  203. visProtected: wrtln('protected');
  204. visPublic: wrtln('public');
  205. visPublished: wrtln('published');
  206. visAutomated: wrtln('automated');
  207. end;
  208. IncIndent;
  209. LastVisibility := CurVisibility;
  210. end;
  211. WriteElement(Member);
  212. end;
  213. DecIndent;
  214. wrtln('end;');
  215. wrtln;
  216. end;
  217. procedure TPasWriter.WriteVariable(AVar: TPasVariable);
  218. begin
  219. if (AVar.Parent.ClassType <> TPasClassType) and
  220. (AVar.Parent.ClassType <> TPasRecordType) then
  221. PrepareDeclSection('var');
  222. wrt(AVar.Name + ': ');
  223. WriteType(AVar.VarType);
  224. wrtln(';');
  225. end;
  226. procedure TPasWriter.WriteProcDecl(AProc: TPasProcedure);
  227. var
  228. i: Integer;
  229. begin
  230. wrt(AProc.TypeName + ' ' + AProc.Name);
  231. if Assigned(AProc.ProcType) and (AProc.ProcType.Args.Count > 0) then
  232. begin
  233. wrt('(');
  234. for i := 0 to AProc.ProcType.Args.Count - 1 do
  235. with TPasArgument(AProc.ProcType.Args[i]) do
  236. begin
  237. if i > 0 then
  238. wrt('; ');
  239. case Access of
  240. argConst: wrt('const ');
  241. argVar: wrt('var ');
  242. end;
  243. wrt(Name);
  244. if Assigned(ArgType) then
  245. begin
  246. wrt(': ');
  247. WriteElement(ArgType);
  248. end;
  249. if Value <> '' then
  250. wrt(' = ' + Value);
  251. end;
  252. wrt(')');
  253. end;
  254. if Assigned(AProc.ProcType) and
  255. (AProc.ProcType.ClassType = TPasFunctionType) then
  256. begin
  257. wrt(': ');
  258. WriteElement(TPasFunctionType(AProc.ProcType).ResultEl.ResultType);
  259. end;
  260. wrt(';');
  261. if AProc.IsVirtual then
  262. wrt(' virtual;');
  263. if AProc.IsDynamic then
  264. wrt(' dynamic;');
  265. if AProc.IsAbstract then
  266. wrt(' abstract;');
  267. if AProc.IsOverride then
  268. wrt(' override;');
  269. if AProc.IsOverload then
  270. wrt(' overload;');
  271. if AProc.IsReintroduced then
  272. wrt(' reintroduce;');
  273. if AProc.IsStatic then
  274. wrt(' static;');
  275. // !!!: Not handled: Message, calling conventions
  276. wrtln;
  277. end;
  278. procedure TPasWriter.WriteProcImpl(AProc: TPasProcedureImpl);
  279. var
  280. i: Integer;
  281. begin
  282. PrepareDeclSection('');
  283. wrt(AProc.TypeName + ' ');
  284. if AProc.Parent.ClassType = TPasClassType then
  285. wrt(AProc.Parent.Name + '.');
  286. wrt(AProc.Name);
  287. if Assigned(AProc.ProcType) and (AProc.ProcType.Args.Count > 0) then
  288. begin
  289. wrt('(');
  290. for i := 0 to AProc.ProcType.Args.Count - 1 do
  291. with TPasArgument(AProc.ProcType.Args[i]) do
  292. begin
  293. if i > 0 then
  294. wrt('; ');
  295. case Access of
  296. argConst: wrt('const ');
  297. argVar: wrt('var ');
  298. end;
  299. wrt(Name);
  300. if Assigned(ArgType) then
  301. begin
  302. wrt(': ');
  303. WriteElement(ArgType);
  304. end;
  305. if Value <> '' then
  306. wrt(' = ' + Value);
  307. end;
  308. wrt(')');
  309. end;
  310. if Assigned(AProc.ProcType) and
  311. (AProc.ProcType.ClassType = TPasFunctionType) then
  312. begin
  313. wrt(': ');
  314. WriteElement(TPasFunctionType(AProc.ProcType).ResultEl.ResultType);
  315. end;
  316. wrtln(';');
  317. IncDeclSectionLevel;
  318. for i := 0 to AProc.Locals.Count - 1 do
  319. begin
  320. if TPasElement(AProc.Locals[i]).InheritsFrom(TPasProcedureImpl) then
  321. begin
  322. IncIndent;
  323. if (i = 0) or not
  324. TPasElement(AProc.Locals[i - 1]).InheritsFrom(TPasProcedureImpl) then
  325. wrtln;
  326. end;
  327. WriteElement(TPasElement(AProc.Locals[i]));
  328. if TPasElement(AProc.Locals[i]).InheritsFrom(TPasProcedureImpl) then
  329. DecIndent;
  330. end;
  331. DecDeclSectionLevel;
  332. wrtln('begin');
  333. IncIndent;
  334. if Assigned(AProc.Body) then
  335. WriteImplBlock(AProc.Body);
  336. DecIndent;
  337. wrtln('end;');
  338. wrtln;
  339. end;
  340. procedure TPasWriter.WriteProperty(AProp: TPasProperty);
  341. var
  342. i: Integer;
  343. begin
  344. wrt('property ' + AProp.Name);
  345. if AProp.Args.Count > 0 then
  346. begin
  347. wrt('[');
  348. for i := 0 to AProp.Args.Count - 1 do;
  349. // !!!: Create WriteArgument method and call it here
  350. wrt(']');
  351. end;
  352. if Assigned(AProp.VarType) then
  353. begin
  354. wrt(': ');
  355. WriteType(AProp.VarType);
  356. end;
  357. if AProp.ReadAccessorName <> '' then
  358. wrt(' read ' + AProp.ReadAccessorName);
  359. if AProp.WriteAccessorName <> '' then
  360. wrt(' write ' + AProp.WriteAccessorName);
  361. if AProp.StoredAccessorName <> '' then
  362. wrt(' stored ' + AProp.StoredAccessorName);
  363. if AProp.DefaultValue <> '' then
  364. wrt(' default ' + AProp.DefaultValue);
  365. if AProp.IsNodefault then
  366. wrt(' nodefault');
  367. if AProp.IsDefault then
  368. wrt('; default');
  369. wrtln(';');
  370. end;
  371. procedure TPasWriter.WriteImplBlock(ABlock: TPasImplBlock);
  372. var
  373. i: Integer;
  374. begin
  375. for i := 0 to ABlock.Elements.Count - 1 do
  376. begin
  377. WriteImplElement(TPasImplElement(ABlock.Elements[i]), False);
  378. if TPasImplElement(ABlock.Elements[i]).ClassType = TPasImplCommand then
  379. wrtln(';');
  380. end;
  381. end;
  382. procedure TPasWriter.WriteImplElement(AElement: TPasImplElement;
  383. AAutoInsertBeginEnd: Boolean);
  384. begin
  385. if AElement.ClassType = TPasImplCommand then
  386. WriteImplCommand(TPasImplCommand(AElement))
  387. else if AElement.ClassType = TPasImplCommands then
  388. begin
  389. DecIndent;
  390. if AAutoInsertBeginEnd then
  391. wrtln('begin');
  392. IncIndent;
  393. WriteImplCommands(TPasImplCommands(AElement));
  394. DecIndent;
  395. if AAutoInsertBeginEnd then
  396. wrtln('end;');
  397. IncIndent;
  398. end else if AElement.ClassType = TPasImplBlock then
  399. begin
  400. DecIndent;
  401. if AAutoInsertBeginEnd then
  402. wrtln('begin');
  403. IncIndent;
  404. WriteImplBlock(TPasImplBlock(AElement));
  405. DecIndent;
  406. if AAutoInsertBeginEnd then
  407. wrtln('end;');
  408. IncIndent;
  409. end else if AElement.ClassType = TPasImplIfElse then
  410. WriteImplIfElse(TPasImplIfElse(AElement))
  411. else if AElement.ClassType = TPasImplForLoop then
  412. WriteImplForLoop(TPasImplForLoop(AElement))
  413. else
  414. raise Exception.Create('Writing not yet implemented for ' +
  415. AElement.ClassName + ' implementation elements');
  416. end;
  417. procedure TPasWriter.WriteImplCommand(ACommand: TPasImplCommand);
  418. begin
  419. wrt(ACommand.Command);
  420. end;
  421. procedure TPasWriter.WriteImplCommands(ACommands: TPasImplCommands);
  422. var
  423. i: Integer;
  424. s: string;
  425. begin
  426. for i := 0 to ACommands.Commands.Count - 1 do
  427. begin
  428. s := ACommands.Commands[i];
  429. if Length(s) > 0 then
  430. if (Length(s) >= 2) and (s[1] = '/') and (s[2] = '/') then
  431. wrtln(s)
  432. else
  433. wrtln(s + ';');
  434. end;
  435. end;
  436. procedure TPasWriter.WriteImplIfElse(AIfElse: TPasImplIfElse);
  437. begin
  438. wrt('if ' + AIfElse.Condition + ' then');
  439. if Assigned(AIfElse.IfBranch) then
  440. begin
  441. wrtln;
  442. if (AIfElse.IfBranch.ClassType = TPasImplCommands) or
  443. (AIfElse.IfBranch.ClassType = TPasImplBlock) then
  444. wrtln('begin');
  445. IncIndent;
  446. WriteImplElement(AIfElse.IfBranch, False);
  447. DecIndent;
  448. if (AIfElse.IfBranch.ClassType = TPasImplCommands) or
  449. (AIfElse.IfBranch.ClassType = TPasImplBlock) then
  450. if Assigned(AIfElse.ElseBranch) then
  451. wrt('end ')
  452. else
  453. wrtln('end;')
  454. else
  455. if Assigned(AIfElse.ElseBranch) then
  456. wrtln;
  457. end else
  458. if not Assigned(AIfElse.ElseBranch) then
  459. wrtln(';')
  460. else
  461. wrtln;
  462. if Assigned(AIfElse.ElseBranch) then
  463. if AIfElse.ElseBranch.ClassType = TPasImplIfElse then
  464. begin
  465. wrt('else ');
  466. WriteImplElement(AIfElse.ElseBranch, True);
  467. end else
  468. begin
  469. wrtln('else');
  470. IncIndent;
  471. WriteImplElement(AIfElse.ElseBranch, True);
  472. if (not Assigned(AIfElse.Parent)) or
  473. (AIfElse.Parent.ClassType <> TPasImplIfElse) or
  474. (TPasImplIfElse(AIfElse.Parent).IfBranch <> AIfElse) then
  475. wrtln(';');
  476. DecIndent;
  477. end;
  478. end;
  479. procedure TPasWriter.WriteImplForLoop(AForLoop: TPasImplForLoop);
  480. begin
  481. wrtln('for ' + AForLoop.Variable.Name + ' := ' + AForLoop.StartValue +
  482. ' to ' + AForLoop.EndValue + ' do');
  483. IncIndent;
  484. WriteImplElement(AForLoop.Body, True);
  485. DecIndent;
  486. if (AForLoop.Body.ClassType <> TPasImplBlock) and
  487. (AForLoop.Body.ClassType <> TPasImplCommands) then
  488. wrtln(';');
  489. end;
  490. procedure TPasWriter.IncIndent;
  491. begin
  492. Indent := Indent + ' ';
  493. end;
  494. procedure TPasWriter.DecIndent;
  495. begin
  496. if Indent = '' then
  497. raise Exception.Create('Internal indent error');
  498. SetLength(Indent, Length(Indent) - 2);
  499. end;
  500. procedure TPasWriter.IncDeclSectionLevel;
  501. var
  502. El: PDeclSectionStackElement;
  503. begin
  504. New(El);
  505. DeclSectionStack.Add(El);
  506. El^.LastDeclSection := CurDeclSection;
  507. El^.LastIndent := Indent;
  508. CurDeclSection := '';
  509. end;
  510. procedure TPasWriter.DecDeclSectionLevel;
  511. var
  512. El: PDeclSectionStackElement;
  513. begin
  514. El := PDeclSectionStackElement(DeclSectionStack[DeclSectionStack.Count - 1]);
  515. DeclSectionStack.Delete(DeclSectionStack.Count - 1);
  516. CurDeclSection := El^.LastDeclSection;
  517. Indent := El^.LastIndent;
  518. Dispose(El);
  519. end;
  520. procedure TPasWriter.PrepareDeclSection(const ADeclSection: string);
  521. begin
  522. if ADeclSection <> CurDeclSection then
  523. begin
  524. if CurDeclsection <> '' then
  525. DecIndent;
  526. if ADeclSection <> '' then
  527. begin
  528. wrtln(ADeclSection);
  529. IncIndent;
  530. end;
  531. CurDeclSection := ADeclSection;
  532. end;
  533. end;
  534. procedure WritePasFile(AElement: TPasElement; const AFilename: string);
  535. var
  536. Stream: TFileStream;
  537. begin
  538. Stream := TFileStream.Create(AFilename, fmCreate);
  539. try
  540. WritePasFile(AElement, Stream);
  541. finally
  542. Stream.Free;
  543. end;
  544. end;
  545. procedure WritePasFile(AElement: TPasElement; AStream: TStream);
  546. var
  547. Writer: TPasWriter;
  548. begin
  549. Writer := TPasWriter.Create(AStream);
  550. try
  551. Writer.WriteElement(AElement);
  552. finally
  553. Writer.Free;
  554. end;
  555. end;
  556. end.