paswrite.pp 15 KB

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