pas2jsfs.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 2018 Michael Van Canneyt
  4. Pascal to Javascript converter class.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************
  11. Abstract:
  12. FileSystem abstraction layer for compiler.
  13. Has only abstract classes with no actual implementation, so it does not actually
  14. interacts with the filesystem.
  15. See Pas2JSFileCache for an actual implementation.
  16. }
  17. {$IFNDEF FPC_DOTTEDUNITS}
  18. unit Pas2JSFS;
  19. {$ENDIF FPC_DOTTEDUNITS}
  20. {$mode objfpc}{$H+}
  21. {$I pas2js_defines.inc}
  22. interface
  23. {$IFDEF FPC_DOTTEDUNITS}
  24. uses
  25. // No NdsApi.Filesystem-dependent units here !
  26. System.Classes, System.SysUtils, Pascal.Scanner, FpJson.Data;
  27. {$ELSE FPC_DOTTEDUNITS}
  28. uses
  29. // No filesystem-dependent units here !
  30. Classes, SysUtils, PScanner, fpjson;
  31. {$ENDIF FPC_DOTTEDUNITS}
  32. const // Messages
  33. nUsingPath = 104; sUsingPath = 'Using %s: "%s"';
  34. nFolderNotFound = 105; sFolderNotFound = '%s not found: %s';
  35. nIncludeSearch = 201; sIncludeSearch = 'Include file search: %s';
  36. nUnitSearch = 202; sUnitSearch = 'Unitsearch: %s';
  37. nSearchingFileFound = 203; sSearchingFileFound = 'Searching file: %s... found';
  38. nSearchingFileNotFound = 204; sSearchingFileNotFound = 'Searching file: %s... not found';
  39. nDuplicateFileFound = 205; sDuplicateFileFound = 'Duplicate file found: "%s" and "%s"';
  40. nCustomJSFileNotFound = 206; sCustomJSFileNotFound = 'custom JS file not found: "%s"';
  41. Type
  42. // Forward definitions
  43. EPas2jsFS = Class(Exception);
  44. TPas2jsFile = class;
  45. TSourceLineReader = class;
  46. TPas2jsFSResolver = class;
  47. TPas2JSFS = Class;
  48. { TSourceLineReader }
  49. TSourceLineReader = class(TLineReader)
  50. private
  51. FIsEOF: boolean;
  52. FLineNumber: integer;
  53. FSource: string;
  54. FSrcPos: integer;
  55. Protected
  56. Procedure IncLineNumber; virtual;
  57. property Source: string read FSource;
  58. property SrcPos: integer read FSrcPos;
  59. public
  60. Constructor Create(Const aFileName, aSource: String); overload;
  61. function IsEOF: Boolean; override;
  62. function ReadLine: TPasScannerString; override;
  63. property LineNumber: integer read FLineNumber;
  64. end;
  65. TP2jsFSOption = (
  66. caoShowFullFilenames,
  67. caoShowTriedUsedFiles,
  68. caoSearchLikeFPC,
  69. caoStrictFileCase
  70. );
  71. TP2jsFSOptions = set of TP2jsFSOption;
  72. TKeyCompareType = (kcFilename,kcUnitName);
  73. { TPas2JSFS }
  74. TPas2JSFS = Class
  75. Private
  76. FOptions: TP2jsFSOptions;
  77. FReadLineCounter: SizeInt;
  78. FDefaultOutputPath: string;
  79. FUnitOutputPath: string;
  80. procedure SetOptionFromIndex(AIndex: Integer; AValue: boolean);
  81. procedure SetDefaultOutputPath(AValue: string);
  82. procedure SetUnitOutputPath(AValue: string);
  83. Protected
  84. // Not to be overridden
  85. procedure SetOption(Flag: TP2jsFSOption; Enable: boolean);
  86. Function OptionIsSet(Index: Integer): Boolean;
  87. Protected
  88. // Protected Abstract. Must be overridden
  89. function FindSourceFileName(const aFilename: string): String; virtual; abstract;
  90. Public
  91. // Public Abstract. Must be overridden
  92. function FindResourceFileName(const aFilename, ModuleDir: string): String; virtual; abstract;
  93. function FindIncludeFileName(const aFilename, SrcDir, ModuleDir: string; Mode: TModeSwitch): String; virtual; abstract;
  94. function LoadFile(Filename: string; Binary: boolean = false): TPas2jsFile; virtual; abstract;
  95. Function FileExists(Const aFileName: String): Boolean; virtual; abstract;
  96. function FindUnitJSFileName(const aUnitFilename: string): String; virtual; abstract;
  97. function FindCustomJSFileName(const aFilename: string): String; virtual; abstract;
  98. function FindUnitFileName(const aUnitname, InFilename, ModuleDir: string; out IsForeign: boolean): String; virtual; abstract;
  99. procedure SaveToFile(ms: TFPJSStream; Filename: string); virtual; abstract;
  100. function PCUExists(var aFileName: string): Boolean; virtual;
  101. procedure GetPCUDirs(aList: TStrings; const aBaseDir: String); virtual;
  102. Public
  103. // Public, may be overridden
  104. Function SameFileName(Const File1,File2: String): Boolean; virtual;
  105. Function File1IsNewer(Const File1,File2: String): Boolean; virtual;
  106. function ExpandDirectory(const Filename: string): string; virtual;
  107. function ExpandFileName(const Filename: string): string; virtual;
  108. function ExpandExecutable(const Filename: string): string; virtual;
  109. Function FormatPath(Const aFileName: string): String; virtual;
  110. Function DirectoryExists(Const aDirectory: string): boolean; virtual;
  111. function TryCreateRelativePath(const Filename, BaseDirectory: String;
  112. UsePointDirectory, AlwaysRequireSharedBaseFolder: boolean; out RelPath: String): Boolean; virtual;
  113. procedure DeleteDuplicateFiles(List: TStrings); virtual;
  114. function IndexOfFile(FileList: TStrings; aFilename: string; Start: integer = 0): integer; virtual;// -1 if not found
  115. Procedure WriteFoldersAndSearchPaths; virtual;
  116. function CreateResolver: TPas2jsFSResolver; virtual;
  117. // On success, return '', On error, return error message.
  118. Function AddForeignUnitPath(Const aValue: String; FromCmdLine: Boolean): String; virtual;
  119. Function HandleOptionPaths(C: AnsiChar; aValue: String; FromCmdLine: Boolean): String; virtual;
  120. Public
  121. Constructor Create; virtual;
  122. Procedure Reset; virtual;
  123. Procedure IncReadLineCounter;
  124. property ReadLineCounter: SizeInt read FReadLineCounter write FReadLineCounter;
  125. property Options: TP2jsFSOptions read FOptions write FOptions;
  126. property ShowFullPaths: boolean Index 0 Read OptionIsSet Write SetOptionFromIndex;
  127. property ShowTriedUsedFiles: boolean Index 1 read OptionIsSet Write SetOptionFromIndex;
  128. property SearchLikeFPC: boolean index 2 read OptionIsSet Write SetOptionFromIndex;
  129. Property StrictFileCase: Boolean Index 3 Read OptionIsSet Write SetOptionFromIndex;
  130. property MainOutputPath: string read FDefaultOutputPath write SetDefaultOutputPath; // includes trailing pathdelim
  131. property UnitOutputPath: string read FUnitOutputPath write SetUnitOutputPath; // includes trailing pathdelim
  132. end;
  133. { TPas2jsFile }
  134. TPas2jsFile = class
  135. private
  136. FAllowSrcMap: boolean;
  137. FFilename: string;
  138. FFS: TPas2JSFS;
  139. FSource: string;
  140. Protected
  141. Procedure SetSource(aSource: String);
  142. public
  143. constructor Create(aFS: TPas2jsFS; const aFilename: string);
  144. function CreateLineReader(RaiseOnError: boolean): TSourceLineReader; virtual; abstract;
  145. function Load(RaiseOnError: boolean; Binary: boolean): boolean; virtual; abstract;
  146. property Source: string read FSource; // UTF-8 without BOM or Binary
  147. property FS: TPas2JSFS Read FFS;
  148. property Filename: string read FFilename;
  149. property AllowSrcMap: boolean read FAllowSrcMap write FAllowSrcMap;
  150. end;
  151. { TPas2jsFSResolver }
  152. TPas2jsFSResolver = class({$IFDEF HASFILESYSTEM}TFileResolver{$ELSE}TBaseFileResolver{$ENDIF})
  153. private
  154. FFS: TPas2jsFS;
  155. public
  156. constructor Create(aFS: TPas2jsFS); reintroduce;
  157. // Redirect all calls to FS.
  158. function FindResourceFileName(const aFilename: string): String; override;
  159. function FindIncludeFileName(const aFilename: string): String; override;
  160. function FindIncludeFile(const aFilename: string): TLineReader; override;
  161. function FindSourceFile(const aFilename: string): TLineReader; override;
  162. property FS: TPas2jsFS read FFS;
  163. end;
  164. Const
  165. p2jsfcoCaption: array[TP2jsFSOption] of string = (
  166. // only used by experts, no need for resourcestrings
  167. 'Show full filenames',
  168. 'Show tried/used files',
  169. 'Search files like FPC',
  170. 'Strict file case'
  171. );
  172. // 'Combine all JavaScript into main file',
  173. EncodingBinary = 'Binary';
  174. DefaultPas2jsFSOptions = [];
  175. implementation
  176. // No filesystem-dependent units here !
  177. { TPas2JSFS }
  178. procedure TPas2JSFS.SetOptionFromIndex(AIndex: Integer; AValue: boolean);
  179. begin
  180. SetOption(TP2jsFSOption(aIndex),aValue);
  181. end;
  182. procedure TPas2JSFS.SetOption(Flag: TP2jsFSOption; Enable: boolean);
  183. begin
  184. if Enable then
  185. Include(FOptions,Flag)
  186. else
  187. Exclude(FOptions,Flag);
  188. end;
  189. function TPas2JSFS.OptionIsSet(Index: Integer): Boolean;
  190. begin
  191. Result:=TP2jsFSOption(Index) in FOptions;
  192. end;
  193. function TPas2JSFS.PCUExists(var aFileName: string): Boolean;
  194. begin
  195. Result:=Self.FileExists(aFileName);
  196. end;
  197. procedure TPas2JSFS.GetPCUDirs(aList: TStrings; const aBaseDir: String);
  198. begin
  199. if UnitOutputPath<>'' then
  200. aList.Add(UnitOutputPath);
  201. aList.Add(aBaseDir);
  202. end;
  203. function TPas2JSFS.SameFileName(const File1, File2: String): Boolean;
  204. begin
  205. Result:=CompareText(File1,File2)=0;
  206. end;
  207. function TPas2JSFS.File1IsNewer(const File1, File2: String): Boolean;
  208. begin
  209. Result:=False;
  210. if File1=File2 then ;
  211. end;
  212. function TPas2JSFS.ExpandDirectory(const Filename: string): string;
  213. begin
  214. Result:=FileName;
  215. end;
  216. function TPas2JSFS.ExpandFileName(const Filename: string): string;
  217. begin
  218. Result:=FileName;
  219. end;
  220. function TPas2JSFS.ExpandExecutable(const Filename: string): string;
  221. begin
  222. Result:=FileName
  223. end;
  224. function TPas2JSFS.FormatPath(const aFileName: string): String;
  225. begin
  226. Result:=aFileName;
  227. end;
  228. function TPas2JSFS.DirectoryExists(const aDirectory: string): boolean;
  229. begin
  230. Result:=aDirectory='';
  231. end;
  232. function TPas2JSFS.TryCreateRelativePath(const Filename, BaseDirectory: String;
  233. UsePointDirectory, AlwaysRequireSharedBaseFolder: boolean; out RelPath: String
  234. ): Boolean;
  235. begin
  236. Result:=True;
  237. RelPath:=FileName;
  238. if (BaseDirectory='') or UsePointDirectory or AlwaysRequireSharedBaseFolder then ;
  239. end;
  240. procedure TPas2JSFS.DeleteDuplicateFiles(List: TStrings);
  241. var
  242. i, j: Integer;
  243. begin
  244. for i:=0 to List.Count-2 do
  245. for j:=List.Count-1 downto i+1 do
  246. if SameFileName(List[i],List[j]) then
  247. List.Delete(j);
  248. end;
  249. function TPas2JSFS.IndexOfFile(FileList: TStrings; aFilename: string;
  250. Start: integer): integer;
  251. var
  252. i: Integer;
  253. begin
  254. if FileList<>nil then
  255. for i:=Start to FileList.Count-1 do
  256. if SameFileName(FileList[i],aFilename) then exit(i);
  257. Result:=-1;
  258. end;
  259. procedure TPas2JSFS.WriteFoldersAndSearchPaths;
  260. begin
  261. // Do nothing
  262. end;
  263. function TPas2JSFS.CreateResolver: TPas2jsFSResolver;
  264. begin
  265. Result:=TPas2jsFSResolver.Create(Self);
  266. end;
  267. function TPas2JSFS.AddForeignUnitPath(const aValue: String; FromCmdLine: Boolean): String;
  268. begin
  269. Result:='';
  270. if (aValue='') or FromCmdLine then ;
  271. end;
  272. function TPas2JSFS.HandleOptionPaths(C: AnsiChar; aValue: String; FromCmdLine: Boolean): String;
  273. begin
  274. Result:='Invalid parameter: -F'+C+aValue;
  275. if FromCmdLine then ;
  276. end;
  277. constructor TPas2JSFS.Create;
  278. begin
  279. FOptions:=DefaultPas2jsFSOptions;
  280. end;
  281. procedure TPas2JSFS.Reset;
  282. begin
  283. FReadLineCounter:=0;
  284. FUnitOutputPath:='';
  285. FDefaultOutputPath:='';
  286. end;
  287. procedure TPas2JSFS.IncReadLineCounter;
  288. begin
  289. Inc(FReadLineCounter);
  290. end;
  291. procedure TPas2JSFS.SetDefaultOutputPath(AValue: string);
  292. begin
  293. AValue:=ExpandDirectory(AValue);
  294. if FDefaultOutputPath=AValue then Exit;
  295. FDefaultOutputPath:=AValue;
  296. end;
  297. procedure TPas2JSFS.SetUnitOutputPath(AValue: string);
  298. begin
  299. AValue:=ExpandDirectory(AValue);
  300. if FUnitOutputPath=AValue then Exit;
  301. FUnitOutputPath:=AValue;
  302. end;
  303. { TPas2jsFile }
  304. procedure TPas2jsFile.SetSource(aSource: String);
  305. begin
  306. FSource:=ASource;
  307. end;
  308. constructor TPas2jsFile.Create(aFS: TPas2jsFS; const aFilename: string);
  309. begin
  310. FFS:=aFS;
  311. FFileName:=aFileName;
  312. end;
  313. procedure TSourceLineReader.IncLineNumber;
  314. begin
  315. inc(FLineNumber);
  316. end;
  317. Constructor TSourceLineReader.Create(Const aFileName, aSource: String);
  318. begin
  319. Inherited Create(aFileName);
  320. FSource:=aSource;
  321. FSrcPos:=1;
  322. FIsEOF:=FSource='';
  323. end;
  324. function TSourceLineReader.IsEOF: Boolean;
  325. begin
  326. Result:=FIsEOF;
  327. end;
  328. function TSourceLineReader.ReadLine: tpasscannerstring;
  329. var
  330. S: string;
  331. p, SrcLen: integer;
  332. procedure GetLine;
  333. var
  334. l: SizeInt;
  335. begin
  336. l:=p-FSrcPos;
  337. Result:=copy(S,FSrcPos,l);
  338. FSrcPos:=p;
  339. IncLineNumber;
  340. //writeln('GetLine "',Result,'"');
  341. end;
  342. begin
  343. if FIsEOF then exit('');
  344. S:=Source;
  345. SrcLen:=length(S);
  346. p:=FSrcPos;
  347. while p<=SrcLen do
  348. case S[p] of
  349. #10,#13:
  350. begin
  351. GetLine;
  352. inc(p);
  353. if (p<=SrcLen) and (S[p] in [#10,#13]) and (S[p]<>S[p-1]) then
  354. inc(p);
  355. if p>SrcLen then
  356. FIsEOF:=true;
  357. FSrcPos:=p;
  358. exit;
  359. end;
  360. else
  361. inc(p);
  362. end;
  363. FIsEOF:=true;
  364. GetLine;
  365. end;
  366. function TPas2jsFSResolver.FindIncludeFile(const aFilename: string): TLineReader;
  367. var
  368. Filename: String;
  369. begin
  370. Result:=nil;
  371. Filename:=FS.FindIncludeFileName(aFilename,BaseDirectory,ModuleDirectory,Mode);
  372. if Filename='' then exit;
  373. try
  374. Result:=FindSourceFile(Filename);
  375. except
  376. // error is shown in the scanner, which has the context information
  377. end;
  378. end;
  379. constructor TPas2jsFSResolver.Create(aFS: TPas2jsFS);
  380. begin
  381. Inherited Create;
  382. FFS:=aFS;
  383. end;
  384. function TPas2jsFSResolver.FindResourceFileName(const aFilename: string): String;
  385. begin
  386. Result:=FS.FindResourceFileName(aFilename,BaseDirectory);
  387. end;
  388. function TPas2jsFSResolver.FindIncludeFileName(const aFilename: string): String;
  389. begin
  390. Result:=FS.FindIncludeFileName(aFilename,BaseDirectory,ModuleDirectory,Mode);
  391. end;
  392. function TPas2jsFSResolver.FindSourceFile(const aFilename: string): TLineReader;
  393. var
  394. CurFilename: String;
  395. begin
  396. CurFilename:=FS.FindSourceFileName(aFileName);
  397. Result:=FS.LoadFile(CurFilename).CreateLineReader(false);
  398. end;
  399. end.