paswrite.pp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  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. // !!!: Not handled: Message, calling conventions
  270. wrtln;
  271. end;
  272. procedure TPasWriter.WriteProcImpl(AProc: TPasProcedureImpl);
  273. var
  274. i: Integer;
  275. begin
  276. PrepareDeclSection('');
  277. wrt(AProc.TypeName + ' ');
  278. if AProc.Parent.ClassType = TPasClassType then
  279. wrt(AProc.Parent.Name + '.');
  280. wrt(AProc.Name);
  281. if Assigned(AProc.ProcType) and (AProc.ProcType.Args.Count > 0) then
  282. begin
  283. wrt('(');
  284. for i := 0 to AProc.ProcType.Args.Count - 1 do
  285. with TPasArgument(AProc.ProcType.Args[i]) do
  286. begin
  287. if i > 0 then
  288. wrt('; ');
  289. case Access of
  290. argConst: wrt('const ');
  291. argVar: wrt('var ');
  292. end;
  293. wrt(Name);
  294. if Assigned(ArgType) then
  295. begin
  296. wrt(': ');
  297. WriteElement(ArgType);
  298. end;
  299. if Value <> '' then
  300. wrt(' = ' + Value);
  301. end;
  302. wrt(')');
  303. end;
  304. if Assigned(AProc.ProcType) and
  305. (AProc.ProcType.ClassType = TPasFunctionType) then
  306. begin
  307. wrt(': ');
  308. WriteElement(TPasFunctionType(AProc.ProcType).ResultEl.ResultType);
  309. end;
  310. wrtln(';');
  311. IncDeclSectionLevel;
  312. for i := 0 to AProc.Locals.Count - 1 do
  313. begin
  314. if TPasElement(AProc.Locals[i]).InheritsFrom(TPasProcedureImpl) then
  315. begin
  316. IncIndent;
  317. if (i = 0) or not
  318. TPasElement(AProc.Locals[i - 1]).InheritsFrom(TPasProcedureImpl) then
  319. wrtln;
  320. end;
  321. WriteElement(TPasElement(AProc.Locals[i]));
  322. if TPasElement(AProc.Locals[i]).InheritsFrom(TPasProcedureImpl) then
  323. DecIndent;
  324. end;
  325. DecDeclSectionLevel;
  326. wrtln('begin');
  327. IncIndent;
  328. if Assigned(AProc.Body) then
  329. WriteImplBlock(AProc.Body);
  330. DecIndent;
  331. wrtln('end;');
  332. wrtln;
  333. end;
  334. procedure TPasWriter.WriteProperty(AProp: TPasProperty);
  335. var
  336. i: Integer;
  337. begin
  338. wrt('property ' + AProp.Name);
  339. if AProp.Args.Count > 0 then
  340. begin
  341. wrt('[');
  342. for i := 0 to AProp.Args.Count - 1 do;
  343. // !!!: Create WriteArgument method and call it here
  344. wrt(']');
  345. end;
  346. if Assigned(AProp.VarType) then
  347. begin
  348. wrt(': ');
  349. WriteType(AProp.VarType);
  350. end;
  351. if AProp.ReadAccessorName <> '' then
  352. wrt(' read ' + AProp.ReadAccessorName);
  353. if AProp.WriteAccessorName <> '' then
  354. wrt(' write ' + AProp.WriteAccessorName);
  355. if AProp.StoredAccessorName <> '' then
  356. wrt(' stored ' + AProp.StoredAccessorName);
  357. if AProp.DefaultValue <> '' then
  358. wrt(' default ' + AProp.DefaultValue);
  359. if AProp.IsNodefault then
  360. wrt(' nodefault');
  361. if AProp.IsDefault then
  362. wrt('; default');
  363. wrtln(';');
  364. end;
  365. procedure TPasWriter.WriteImplBlock(ABlock: TPasImplBlock);
  366. var
  367. i: Integer;
  368. begin
  369. for i := 0 to ABlock.Elements.Count - 1 do
  370. begin
  371. WriteImplElement(TPasImplElement(ABlock.Elements[i]), False);
  372. if TPasImplElement(ABlock.Elements[i]).ClassType = TPasImplCommand then
  373. wrtln(';');
  374. end;
  375. end;
  376. procedure TPasWriter.WriteImplElement(AElement: TPasImplElement;
  377. AAutoInsertBeginEnd: Boolean);
  378. begin
  379. if AElement.ClassType = TPasImplCommand then
  380. WriteImplCommand(TPasImplCommand(AElement))
  381. else if AElement.ClassType = TPasImplCommands then
  382. begin
  383. DecIndent;
  384. if AAutoInsertBeginEnd then
  385. wrtln('begin');
  386. IncIndent;
  387. WriteImplCommands(TPasImplCommands(AElement));
  388. DecIndent;
  389. if AAutoInsertBeginEnd then
  390. wrtln('end;');
  391. IncIndent;
  392. end else if AElement.ClassType = TPasImplBlock then
  393. begin
  394. DecIndent;
  395. if AAutoInsertBeginEnd then
  396. wrtln('begin');
  397. IncIndent;
  398. WriteImplBlock(TPasImplBlock(AElement));
  399. DecIndent;
  400. if AAutoInsertBeginEnd then
  401. wrtln('end;');
  402. IncIndent;
  403. end else if AElement.ClassType = TPasImplIfElse then
  404. WriteImplIfElse(TPasImplIfElse(AElement))
  405. else if AElement.ClassType = TPasImplForLoop then
  406. WriteImplForLoop(TPasImplForLoop(AElement))
  407. else
  408. raise Exception.Create('Writing not yet implemented for ' +
  409. AElement.ClassName + ' implementation elements');
  410. end;
  411. procedure TPasWriter.WriteImplCommand(ACommand: TPasImplCommand);
  412. begin
  413. wrt(ACommand.Command);
  414. end;
  415. procedure TPasWriter.WriteImplCommands(ACommands: TPasImplCommands);
  416. var
  417. i: Integer;
  418. s: string;
  419. begin
  420. for i := 0 to ACommands.Commands.Count - 1 do
  421. begin
  422. s := ACommands.Commands[i];
  423. if Length(s) > 0 then
  424. if (Length(s) >= 2) and (s[1] = '/') and (s[2] = '/') then
  425. wrtln(s)
  426. else
  427. wrtln(s + ';');
  428. end;
  429. end;
  430. procedure TPasWriter.WriteImplIfElse(AIfElse: TPasImplIfElse);
  431. begin
  432. wrt('if ' + AIfElse.Condition + ' then');
  433. if Assigned(AIfElse.IfBranch) then
  434. begin
  435. wrtln;
  436. if (AIfElse.IfBranch.ClassType = TPasImplCommands) or
  437. (AIfElse.IfBranch.ClassType = TPasImplBlock) then
  438. wrtln('begin');
  439. IncIndent;
  440. WriteImplElement(AIfElse.IfBranch, False);
  441. DecIndent;
  442. if (AIfElse.IfBranch.ClassType = TPasImplCommands) or
  443. (AIfElse.IfBranch.ClassType = TPasImplBlock) then
  444. if Assigned(AIfElse.ElseBranch) then
  445. wrt('end ')
  446. else
  447. wrtln('end;')
  448. else
  449. if Assigned(AIfElse.ElseBranch) then
  450. wrtln;
  451. end else
  452. if not Assigned(AIfElse.ElseBranch) then
  453. wrtln(';')
  454. else
  455. wrtln;
  456. if Assigned(AIfElse.ElseBranch) then
  457. if AIfElse.ElseBranch.ClassType = TPasImplIfElse then
  458. begin
  459. wrt('else ');
  460. WriteImplElement(AIfElse.ElseBranch, True);
  461. end else
  462. begin
  463. wrtln('else');
  464. IncIndent;
  465. WriteImplElement(AIfElse.ElseBranch, True);
  466. if (not Assigned(AIfElse.Parent)) or
  467. (AIfElse.Parent.ClassType <> TPasImplIfElse) or
  468. (TPasImplIfElse(AIfElse.Parent).IfBranch <> AIfElse) then
  469. wrtln(';');
  470. DecIndent;
  471. end;
  472. end;
  473. procedure TPasWriter.WriteImplForLoop(AForLoop: TPasImplForLoop);
  474. begin
  475. wrtln('for ' + AForLoop.Variable.Name + ' := ' + AForLoop.StartValue +
  476. ' to ' + AForLoop.EndValue + ' do');
  477. IncIndent;
  478. WriteImplElement(AForLoop.Body, True);
  479. DecIndent;
  480. if (AForLoop.Body.ClassType <> TPasImplBlock) and
  481. (AForLoop.Body.ClassType <> TPasImplCommands) then
  482. wrtln(';');
  483. end;
  484. procedure TPasWriter.IncIndent;
  485. begin
  486. Indent := Indent + ' ';
  487. end;
  488. procedure TPasWriter.DecIndent;
  489. begin
  490. if Indent = '' then
  491. raise Exception.Create('Internal indent error');
  492. SetLength(Indent, Length(Indent) - 2);
  493. end;
  494. procedure TPasWriter.IncDeclSectionLevel;
  495. var
  496. El: PDeclSectionStackElement;
  497. begin
  498. New(El);
  499. DeclSectionStack.Add(El);
  500. El^.LastDeclSection := CurDeclSection;
  501. El^.LastIndent := Indent;
  502. CurDeclSection := '';
  503. end;
  504. procedure TPasWriter.DecDeclSectionLevel;
  505. var
  506. El: PDeclSectionStackElement;
  507. begin
  508. El := PDeclSectionStackElement(DeclSectionStack[DeclSectionStack.Count - 1]);
  509. DeclSectionStack.Delete(DeclSectionStack.Count - 1);
  510. CurDeclSection := El^.LastDeclSection;
  511. Indent := El^.LastIndent;
  512. Dispose(El);
  513. end;
  514. procedure TPasWriter.PrepareDeclSection(const ADeclSection: string);
  515. begin
  516. if ADeclSection <> CurDeclSection then
  517. begin
  518. if CurDeclsection <> '' then
  519. DecIndent;
  520. if ADeclSection <> '' then
  521. begin
  522. wrtln(ADeclSection);
  523. IncIndent;
  524. end;
  525. CurDeclSection := ADeclSection;
  526. end;
  527. end;
  528. procedure WritePasFile(AElement: TPasElement; const AFilename: string);
  529. var
  530. Stream: TFileStream;
  531. begin
  532. Stream := TFileStream.Create(AFilename, fmCreate);
  533. try
  534. WritePasFile(AElement, Stream);
  535. finally
  536. Stream.Free;
  537. end;
  538. end;
  539. procedure WritePasFile(AElement: TPasElement; AStream: TStream);
  540. var
  541. Writer: TPasWriter;
  542. begin
  543. Writer := TPasWriter.Create(AStream);
  544. try
  545. Writer.WriteElement(AElement);
  546. finally
  547. Writer.Free;
  548. end;
  549. end;
  550. end.