pasrewrite.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. program pasrewrite;
  2. {$mode objfpc}
  3. {$H+}
  4. uses SysUtils, inifiles, strutils, Classes, Pscanner,PParser, PasTree, paswrite, custapp, iostream;
  5. //# types the parser needs
  6. type
  7. { We have to override abstract TPasTreeContainer methods.
  8. See utils/fpdoc/dglobals.pp for an implementation of TFPDocEngine,
  9. a "real" engine. }
  10. TSimpleEngine = class(TPasTreeContainer)
  11. public
  12. function CreateElement(AClass: TPTreeElement; const AName: String;
  13. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  14. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  15. override;
  16. function FindElement(const AName: String): TPasElement; override;
  17. end;
  18. { TPasRewriteApplication }
  19. TPasRewriteApplication = Class(TCustomApplication)
  20. Private
  21. FHeaderFile : String;
  22. FDefines : TStrings;
  23. FLineNumberWidth,
  24. FIndentSize : Integer;
  25. FOptions : TPasWriterOptions;
  26. FForwardClasses,
  27. FExtraUnits,
  28. cmdl,
  29. ConfigFile,
  30. filename,
  31. TargetOS,
  32. TargetCPU : string;
  33. function GetModule: TPasModule;
  34. procedure PrintUsage(S: String);
  35. procedure ReadConfig(const aFileName: String);
  36. procedure ReadConfig(const aIni: TIniFile);
  37. procedure WriteModule(M: TPasModule);
  38. Protected
  39. function ParseOptions : Boolean;
  40. Procedure DoRun; override;
  41. Public
  42. Constructor Create(AOwner : TComponent); override;
  43. Destructor Destroy; override;
  44. end;
  45. { TSimpleEngine }
  46. function TSimpleEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  47. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  48. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  49. begin
  50. Result := AClass.Create(AName, AParent);
  51. Result.Visibility := AVisibility;
  52. Result.SourceFilename := ASourceFilename;
  53. Result.SourceLinenumber := ASourceLinenumber;
  54. end;
  55. function TSimpleEngine.FindElement(const AName: String): TPasElement;
  56. begin
  57. { dummy implementation, see TFPDocEngine.FindElement for a real example }
  58. Result := nil;
  59. end;
  60. { TPasRewriteApplication }
  61. procedure TPasRewriteApplication.PrintUsage(S : String);
  62. begin
  63. if S<>'' then
  64. Writeln('Error : ',S);
  65. writeln('usage: pasrewrite options');
  66. writeln;
  67. writeln('Where options is one or more of');
  68. writeln('-s --os=OS Set OS, one of WINDOWS, LINUX (default), FREEBSD, NETBSD,');
  69. writeln(' SUNOS, BEOS, QNX, GO32V2');
  70. writeln('-u --cpu=CPU Set CPU = i386 (default), x86_64');
  71. writeln('-x --extra=units Comma-separated list of extra units to be added to uses list.');
  72. writeln('-i --input=cmd Is the commandline for the parser');
  73. writeln('-o --output=file Output file name. If not specified, standard output is assumed ');
  74. Writeln('-t --indent=N Number of characters for indent (default 2)');
  75. Writeln('-c --config=filename Read ini file with configuration');
  76. Writeln('-H --header=filename Add file header using contents of file "filename"');
  77. Writeln('--no-implementation Skip generation of executeable code');
  78. Writeln('--no-externalclass Skip generation of external classes (write as regular class)');
  79. Writeln('--no-externalvar Skip generation of external variables (write as regular variables)');
  80. Writeln('--no-externalfunction Skip generation of external functions (write as regular functions)');
  81. Writeln('-f --forwardclasses[=list]');
  82. Writeln(' Generate forward definitions for list of classes. If empty, for all classes.');
  83. Writeln('-n --add-linenumber Add linenumber to comment in front of each line');
  84. Writeln('-N --add-sourcelinenumber Add source linenumber to comment in front of each line');
  85. Writeln('-w --linenumberwidth Number of digits to pad line numbers (default 4)');
  86. ExitCode:=Ord(S<>'');
  87. end;
  88. function TPasRewriteApplication.ParseOptions : Boolean;
  89. Var
  90. S : String;
  91. begin
  92. TargetOS:='linux';
  93. TargetCPU:='i386';
  94. FIndentSize:=-1;
  95. FOptions:=[];
  96. Result:=False;
  97. S:=CheckOptions('d:w:fhs:u:i:o:nNt:c:x:',['help','os:','cpu:','input:','output:','indent:','define',
  98. 'no-implementation','no-externalclass',
  99. 'no-externalvar','add-linenumber','add-sourcelinenumber',
  100. 'no-externalfunction','extra:','forwardclasses::',
  101. 'config:','linenumberwidth']);
  102. if (S<>'') or HasOption('h','help') then
  103. begin
  104. PrintUsage(S);
  105. Exit;
  106. end;
  107. // Standard options
  108. cmdl:=GetOptionValue('i','input');
  109. FileName:=GetOptionValue('o','output');
  110. FHeaderFile:=GetOptionValue('H','header');;
  111. if HasOption('s','os') then
  112. TargetOS:=GetOPtionValue('s','os');
  113. if HasOption('u','cpu') then
  114. TargetCPU:=GetOptionValue('u','cpu');
  115. ConfigFile:=GetOptionValue('c','config');
  116. FExtraUnits:=GetOptionValue('x','extra');
  117. // Options
  118. if Hasoption('w','linenumberwidth') then
  119. FLineNumberWidth:=StrToIntDef(GetOptionValue('w','linenumberwidth'),-1);
  120. if Hasoption('n','add-linenumber') then
  121. Include(Foptions,woAddLineNumber);
  122. if Hasoption('N','add-sourcelinenumber') then
  123. Include(Foptions,woAddSourceLineNumber);
  124. if Hasoption('no-implementation') then
  125. Include(Foptions,woNoImplementation);
  126. if Hasoption('no-externalclass') then
  127. Include(Foptions,woNoExternalClass);
  128. if Hasoption('no-externalvar') then
  129. Include(Foptions,woNoExternalVar);
  130. if Hasoption('no-externalfunction') then
  131. Include(Foptions,woNoExternalFunc);
  132. If HasOption('d','define') then
  133. for S in GetOptionValues('d','define') do
  134. FDefines.Add(S);
  135. if Hasoption('f','forwardclasses') then
  136. begin
  137. Include(Foptions,woForwardClasses);
  138. FForwardClasses:=GetOptionValue('f','forwardclasses');
  139. end;
  140. // Indent
  141. if HasOption('t','indent') then
  142. FIndentSize:=StrToIntDef(GetOptionValue('d','indent'),-1);
  143. if (FHeaderFile<>'') and Not FileExists(FheaderFile) then
  144. begin
  145. PrintUsage(Format('Header file "%s"does not exist',[FHeaderFile]));
  146. Exit;
  147. end;
  148. // Check options
  149. Result:=(Cmdl<>'') ;
  150. If Not Result then
  151. PrintUsage('Need input');
  152. end;
  153. { TPasRewriteApplication }
  154. Function TPasRewriteApplication.GetModule : TPasModule;
  155. Var
  156. SE : TSimpleEngine;
  157. FileResolver: TFileResolver;
  158. InputFileName : string;
  159. Parser: TPasParser;
  160. Start, CurPos: PChar;
  161. Scanner: TPascalScanner;
  162. procedure ProcessCmdLinePart;
  163. var
  164. l: Integer;
  165. s: String;
  166. begin
  167. l := CurPos - Start;
  168. SetLength(s, l);
  169. if l > 0 then
  170. Move(Start^, s[1], l)
  171. else
  172. exit;
  173. if (s[1] = '-') and (length(s)>1) then
  174. begin
  175. case s[2] of
  176. 'd': // -d define
  177. Scanner.AddDefine(UpperCase(Copy(s, 3, Length(s))));
  178. 'u': // -u undefine
  179. Scanner.RemoveDefine(UpperCase(Copy(s, 3, Length(s))));
  180. 'F': // -F
  181. if (length(s)>2) and (s[3] = 'i') then // -Fi include path
  182. FileResolver.AddIncludePath(Copy(s, 4, Length(s)));
  183. 'I': // -I include path
  184. FileResolver.AddIncludePath(Copy(s, 3, Length(s)));
  185. 'S': // -S mode
  186. if (length(s)>2) then
  187. begin
  188. l:=3;
  189. While L<=Length(S) do
  190. begin
  191. case S[l] of
  192. 'c' : Scanner.Options:=Scanner.Options+[po_cassignments];
  193. 'd' : Scanner.SetCompilerMode('DELPHI');
  194. '2' : Scanner.SetCompilerMode('OBJFPC');
  195. 'h' : ; // do nothing
  196. end;
  197. inc(l);
  198. end;
  199. end;
  200. 'M' :
  201. begin
  202. delete(S,1,2);
  203. Scanner.SetCompilerMode(S);
  204. end;
  205. end;
  206. end else
  207. if InputFilename <> '' then
  208. raise Exception.Create(SErrMultipleSourceFiles)
  209. else
  210. InputFilename := s;
  211. end;
  212. var
  213. s: String;
  214. begin
  215. try
  216. Result := nil;
  217. FileResolver := nil;
  218. Scanner := nil;
  219. Parser := nil;
  220. SE:=TSimpleEngine.Create;
  221. try
  222. FileResolver := TFileResolver.Create;
  223. FileResolver.UseStreams:=True;
  224. Scanner := TPascalScanner.Create(FileResolver);
  225. Scanner.Options:=[po_keepclassforward,po_AsmWhole];
  226. SCanner.LogEvents:=SE.ScannerLogEvents;
  227. SCanner.OnLog:=SE.Onlog;
  228. Scanner.AddDefine('FPK');
  229. Scanner.AddDefine('FPC');
  230. For S in FDefines do
  231. Scanner.AddDefine(S);
  232. // TargetOS
  233. s := UpperCase(TargetOS);
  234. Scanner.AddDefine(s);
  235. if s = 'LINUX' then
  236. Scanner.AddDefine('UNIX')
  237. else if s = 'FREEBSD' then
  238. begin
  239. Scanner.AddDefine('BSD');
  240. Scanner.AddDefine('UNIX');
  241. end else if s = 'NETBSD' then
  242. begin
  243. Scanner.AddDefine('BSD');
  244. Scanner.AddDefine('UNIX');
  245. end else if s = 'SUNOS' then
  246. begin
  247. Scanner.AddDefine('SOLARIS');
  248. Scanner.AddDefine('UNIX');
  249. end else if s = 'GO32V2' then
  250. Scanner.AddDefine('DPMI')
  251. else if s = 'BEOS' then
  252. Scanner.AddDefine('UNIX')
  253. else if s = 'QNX' then
  254. Scanner.AddDefine('UNIX')
  255. else if s = 'AROS' then
  256. Scanner.AddDefine('HASAMIGA')
  257. else if s = 'MORPHOS' then
  258. Scanner.AddDefine('HASAMIGA')
  259. else if s = 'AMIGA' then
  260. Scanner.AddDefine('HASAMIGA');
  261. // TargetCPU
  262. s := UpperCase(TargetCPU);
  263. Scanner.AddDefine('CPU'+s);
  264. if (s='X86_64') then
  265. Scanner.AddDefine('CPU64')
  266. else
  267. Scanner.AddDefine('CPU32');
  268. Parser := TPasParser.Create(Scanner, FileResolver, SE);
  269. InputFilename := '';
  270. Parser.LogEvents:=SE.ParserLogEvents;
  271. Parser.OnLog:=SE.Onlog;
  272. if cmdl<>'' then
  273. begin
  274. Start := @cmdl[1];
  275. CurPos := Start;
  276. while CurPos[0] <> #0 do
  277. begin
  278. if CurPos[0] = ' ' then
  279. begin
  280. ProcessCmdLinePart;
  281. Start := CurPos + 1;
  282. end;
  283. Inc(CurPos);
  284. end;
  285. ProcessCmdLinePart;
  286. end;
  287. if InputFilename = '' then
  288. raise Exception.Create(SErrNoSourceGiven);
  289. FileResolver.AddIncludePath(ExtractFilePath(InputFileName));
  290. Scanner.OpenFile(InputFilename);
  291. Parser.Options:=Parser.Options+[po_AsmWhole,po_KeepClassForward];
  292. Parser.ParseMain(Result);
  293. finally
  294. Parser.Free;
  295. Scanner.Free;
  296. FileResolver.Free;
  297. SE.Free;
  298. end;
  299. except
  300. on E : EParserError do
  301. begin
  302. writeln(E.message,' line:',E .row,' column:', E .column,' file:',E.filename);
  303. end;
  304. on Ex : Exception do
  305. begin
  306. Writeln(Ex.Message);
  307. end;
  308. end;
  309. end;
  310. procedure TPasRewriteApplication.ReadConfig(const aFileName: String);
  311. Var
  312. ini : TMemIniFile;
  313. begin
  314. ini:=TMemIniFile.Create(AFileName);
  315. try
  316. ReadConfig(Ini);
  317. finally
  318. Ini.Free;
  319. end;
  320. end;
  321. procedure TPasRewriteApplication.ReadConfig(const aIni: TIniFile);
  322. Const
  323. DelChars = [',',' '];
  324. Var
  325. O : TPaswriterOptions;
  326. W,S : String;
  327. I : Integer;
  328. begin
  329. O:=[];
  330. With aIni do
  331. begin
  332. TargetOS:=ReadString('config','targetos',TargetOS);
  333. TargetCPU:=ReadString('config','targetcpu',TargetCPU);
  334. S:=ReadString('config','options','');
  335. if (S<>'') then
  336. For I:=1 to WordCount(S,DelChars) do
  337. begin
  338. W:=LowerCase(ExtractWord(I,S,DelChars));
  339. Case w of
  340. 'noimplementation': Include(O,woNoImplementation);
  341. 'noexternalclass' : Include(O,woNoExternalClass);
  342. 'noexternalvar' : Include(O,woNoExternalVar);
  343. 'noexternalfunction' : Include(O,woNoExternalFunc);
  344. 'forwardclasses' : Include(O,woForwardClasses);
  345. 'addlinenumber': Include(O,woAddLineNumber);
  346. 'addsourcelinenumber': Include(O,woAddSourceLineNumber);
  347. end;
  348. end;
  349. FOptions:=O;
  350. cmdl:=ReadString('config','input',cmdl);
  351. Self.filename:=ReadString('config','output',Self.filename);
  352. FIndentSize:=ReadInteger('config','indentsize',FIndentSize);
  353. FLineNumberWidth:=ReadInteger('config','linenumberwidth',FLineNumberWidth);
  354. FExtraUnits:=ReadString('config','extra',FExtraUnits);
  355. FForwardClasses:=ReadString('config','forwardclasses',FForwardClasses);
  356. S:=ReadString('config','defines','');
  357. if (S<>'') then
  358. For I:=1 to WordCount(S,DelChars) do
  359. FDefines.Add(UpperCase(ExtractWord(I,S,DelChars)));
  360. if (FForwardClasses<>'') then
  361. Include(O,woForwardClasses);
  362. end;
  363. end;
  364. procedure TPasRewriteApplication.WriteModule(M : TPAsModule);
  365. Var
  366. F,H : TStream;
  367. W : TPasWriter;
  368. begin
  369. W:=Nil;
  370. if FileName='' then
  371. F:=TIOStream.Create(iosOutPut)
  372. else
  373. F:=TFileStream.Create(FileName,fmCreate);
  374. try
  375. if (FHeaderFile<>'') then
  376. begin
  377. H:=TFileStream.Create(FHeaderFile,fmOpenRead or fmShareDenyWrite);
  378. try
  379. F.CopyFrom(H,H.Size);
  380. finally
  381. H.Free;
  382. end;
  383. end;
  384. W:=TPasWriter.Create(F);
  385. W.Options:=FOptions;
  386. W.ExtraUnits:=FExtraUnits;
  387. if FIndentSize<>-1 then
  388. W.IndentSize:=FIndentSize;
  389. if FLineNumberWidth>0 then
  390. W.LineNumberWidth:=FLineNumberWidth;
  391. W.ForwardClasses.CommaText:=FForwardClasses;
  392. W.WriteModule(M);
  393. finally
  394. W.Free;
  395. F.Free;
  396. end;
  397. end;
  398. procedure TPasRewriteApplication.DoRun;
  399. Var
  400. M: TPasModule;
  401. begin
  402. Terminate;
  403. TargetOS:='linux';
  404. TargetCPU:='i386';
  405. If not ParseOptions then
  406. exit;
  407. If (ConfigFile<>'') then
  408. ReadConfig(ConfigFile);
  409. M:=GetModule;
  410. if M=Nil then
  411. exit;
  412. try
  413. WriteModule(M);
  414. finally
  415. M.Free;
  416. end;
  417. end;
  418. constructor TPasRewriteApplication.Create(AOwner: TComponent);
  419. begin
  420. inherited Create(AOwner);
  421. FDefines:=TStringList.Create;
  422. end;
  423. destructor TPasRewriteApplication.Destroy;
  424. begin
  425. FreeAndNil(FDefines);
  426. inherited Destroy;
  427. end;
  428. Var
  429. Application : TPasRewriteApplication;
  430. begin
  431. Application:=TPasRewriteApplication.Create(Nil);
  432. Application.Initialize;
  433. Application.Run;
  434. Application.Free;
  435. end.