jswriter.pp 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218
  1. { *********************************************************************
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 2016 Michael Van Canneyt.
  4. Javascript minifier
  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. unit jswriter;
  12. {$i fcl-js.inc}
  13. { $DEFINE DEBUGJSWRITER}
  14. interface
  15. uses
  16. {$ifdef pas2js}
  17. JS,
  18. {$endif}
  19. SysUtils, jsbase, jstree;
  20. Type
  21. {$ifdef pas2js}
  22. TJSWriterString = UnicodeString;
  23. TJSWriterChar = WideChar;
  24. {$else}
  25. TJSWriterString = AnsiString;
  26. TJSWriterChar = AnsiChar;
  27. {$endif}
  28. TTextWriter = class;
  29. TTextWriterWriting = procedure(Sender: TTextWriter) of object;
  30. { TTextWriter }
  31. TTextWriter = Class(TObject)
  32. private
  33. FCurElement: TJSElement;
  34. FCurLine: integer;
  35. FCurColumn: integer;
  36. FLineBreak: string;
  37. FOnWriting: TTextWriterWriting;
  38. protected
  39. Function DoWrite(Const S : TJSWriterString) : Integer; virtual; abstract;
  40. {$ifdef FPC_HAS_CPSTRING}
  41. Function DoWrite(Const S : UnicodeString) : Integer; virtual; abstract;
  42. {$endif}
  43. procedure SetCurElement(const AValue: TJSElement); virtual;
  44. Procedure Writing; virtual; // called before adding new characters
  45. Public
  46. // All functions return the number of bytes copied to output stream.
  47. constructor Create;
  48. {$ifdef FPC_HAS_CPSTRING}
  49. Function Write(Const S : UnicodeString) : Integer;
  50. {$endif}
  51. Function Write(Const S : TJSWriterString) : Integer;
  52. Function WriteLn(Const S : TJSWriterString) : Integer;
  53. Function Write(Const Fmt : TJSWriterString; Args : Array of const) : Integer;
  54. Function WriteLn(Const Fmt : TJSWriterString; Args : Array of const) : Integer;
  55. Function Write(Const Args : Array of const) : Integer;
  56. Function WriteLn(Const Args : Array of const) : Integer;
  57. Property CurLine: integer read FCurLine write FCurLine;
  58. Property CurColumn: integer read FCurColumn write FCurColumn;// char index, not codepoint
  59. Property CurElement: TJSElement read FCurElement write SetCurElement;
  60. Property OnWriting: TTextWriterWriting read FOnWriting write FOnWriting;
  61. Property LineBreak: string read FLineBreak write FLineBreak;
  62. end;
  63. {$ifdef HasFileWriter}
  64. { TFileWriter }
  65. TFileWriter = Class(TTextWriter)
  66. Protected
  67. {$ifdef NodeJS}
  68. {$else}
  69. FFile : Text;
  70. {$endif}
  71. FFileName : String;
  72. Function DoWrite(Const S : TJSWriterString) : Integer; override;
  73. {$ifdef FPC_HAS_CPSTRING}
  74. Function DoWrite(Const S : UnicodeString) : Integer; override;
  75. {$endif}
  76. Public
  77. Constructor Create(Const AFileName : String); reintroduce;
  78. Destructor Destroy; override;
  79. Procedure Flush;
  80. Procedure Close;
  81. Property FileName : String Read FFileName;
  82. end;
  83. {$endif}
  84. TBufferWriter_Buffer = Array of {$ifdef fpc}byte{$else}string{$endif};
  85. { TBufferWriter }
  86. TBufferWriter = Class(TTextWriter)
  87. private type
  88. TBuffer = TBufferWriter_Buffer;
  89. private
  90. FBufPos,
  91. FCapacity: Cardinal;
  92. FBuffer: TBuffer;
  93. function GetAsString: TJSWriterString;
  94. {$ifdef fpc}
  95. function GetBuffer: Pointer;
  96. {$endif}
  97. function GetBufferLength: Integer;
  98. function GetCapacity: Cardinal;
  99. {$ifdef FPC_HAS_CPSTRING}
  100. function GetUnicodeString: UnicodeString;
  101. {$endif}
  102. procedure SetAsString(const AValue: TJSWriterString);
  103. procedure SetCapacity(AValue: Cardinal);
  104. Protected
  105. Function DoWrite(Const S : TJSWriterString) : integer; override;
  106. {$ifdef FPC_HAS_CPSTRING}
  107. Function DoWrite(Const S : UnicodeString) : integer; override;
  108. {$endif}
  109. Public
  110. Constructor Create(Const ACapacity : Cardinal); reintroduce;
  111. {$ifdef fpc}
  112. Procedure SaveToFile(Const AFileName : String);
  113. Property Buffer : Pointer Read GetBuffer;
  114. {$endif}
  115. {$ifdef pas2js}
  116. Property Buffer: TBufferWriter_Buffer read FBuffer;
  117. {$endif}
  118. Property BufferLength : Integer Read GetBufferLength;
  119. Property Capacity : Cardinal Read GetCapacity Write SetCapacity;
  120. Property AsString : TJSWriterString Read GetAsString Write SetAsString;
  121. {$ifdef FPC_HAS_CPSTRING}
  122. Property AsAnsiString : AnsiString Read GetAsString; deprecated 'use AsString instead, fpc 3.3.1';
  123. Property AsUnicodeString : UnicodeString Read GetUnicodeString;
  124. {$endif}
  125. end;
  126. TJSEscapeQuote = (
  127. jseqSingle,
  128. jseqDouble,
  129. jseqBoth
  130. );
  131. { TJSWriter }
  132. TWriteOption = (woCompact,
  133. {$ifdef FPC_HAS_CPSTRING}
  134. woUseUTF8,
  135. {$endif}
  136. woTabIndent,
  137. woEmptyStatementAsComment,
  138. woQuoteElementNames,
  139. woCompactArrayLiterals,
  140. woCompactObjectLiterals,
  141. woCompactArguments);
  142. TWriteOptions = Set of TWriteOption;
  143. TJSWriter = Class
  144. private
  145. FCurIndent : Integer;
  146. FFreeWriter : Boolean;
  147. FIndentChar : Char;
  148. FIndentSize: Byte;
  149. FLastChar: WideChar;
  150. FLinePos : Integer;
  151. FOptions: TWriteOptions;
  152. FSkipCurlyBrackets : Boolean;
  153. FSkipRoundBrackets : Boolean;
  154. FWriter: TTextWriter;
  155. function GetUseUTF8: Boolean;
  156. procedure SetOptions(AValue: TWriteOptions);
  157. Protected
  158. // Helper routines
  159. Procedure Error(Const Msg : TJSWriterString);
  160. Procedure Error(Const Fmt : TJSWriterString; Args : Array of const);
  161. Procedure WriteIndent; // inline;
  162. {$ifdef FPC_HAS_CPSTRING}
  163. Procedure Write(Const U : UnicodeString);
  164. {$endif}
  165. Procedure Write(Const S : TJSWriterString);
  166. Procedure WriteLn(Const S : TJSWriterString);
  167. {$ifdef FPC_HAS_CPSTRING}
  168. Procedure WriteLn(Const U : UnicodeString);
  169. {$endif}
  170. // one per type of statement
  171. Procedure WriteValue(V : TJSValue); virtual;
  172. Procedure WriteRegularExpressionLiteral(El: TJSRegularExpressionLiteral);
  173. Procedure WriteVariableStatement(El: TJSVariableStatement);
  174. Procedure WriteEmptyBlockStatement(El: TJSEmptyBlockStatement); virtual;
  175. Procedure WriteEmptyStatement(El: TJSEmptyStatement);virtual;
  176. Procedure WriteLiteral(El: TJSLiteral);virtual;
  177. Procedure WriteArrayLiteral(El: TJSArrayLiteral);virtual;
  178. Procedure WriteObjectLiteral(El: TJSObjectLiteral);virtual;
  179. Procedure WriteMemberExpression(El: TJSMemberExpression);virtual;
  180. Procedure WriteCallExpression(El: TJSCallExpression);virtual;
  181. Procedure WriteSwitchStatement(El: TJSSwitchStatement);virtual;
  182. Procedure WriteUnary(El: TJSUnary);virtual;
  183. Procedure WriteAssignStatement(El: TJSAssignStatement);virtual;
  184. Procedure WriteForInStatement(El: TJSForInStatement);virtual;
  185. Procedure WriteWhileStatement(El: TJSWhileStatement);virtual;
  186. Procedure WriteForStatement(El: TJSForStatement);virtual;
  187. Procedure WriteIfStatement(El: TJSIfStatement);virtual;
  188. Procedure WriteSourceElements(El: TJSSourceElements);virtual;
  189. Procedure WriteStatementList(El: TJSStatementList);virtual;
  190. Procedure WriteTryStatement(El: TJSTryStatement);virtual;
  191. Procedure WriteVarDeclaration(El: TJSVarDeclaration);virtual;
  192. Procedure WriteWithStatement(El: TJSWithStatement);virtual;
  193. Procedure WriteVarDeclarationList(El: TJSVariableDeclarationList);virtual;
  194. Procedure WriteConditionalExpression(El: TJSConditionalExpression);virtual;
  195. Procedure WriteFunctionBody(El: TJSFunctionBody);virtual;
  196. Procedure WriteFunctionDeclarationStatement(El: TJSFunctionDeclarationStatement);virtual;
  197. Procedure WriteLabeledStatement(El: TJSLabeledStatement);virtual;
  198. Procedure WriteReturnStatement(El: TJSReturnStatement);virtual;
  199. Procedure WriteTargetStatement(El: TJSTargetStatement);virtual;
  200. Procedure WriteFuncDef(FD: TJSFuncDef);virtual;
  201. Procedure WritePrimaryExpression(El: TJSPrimaryExpression);virtual;
  202. Procedure WriteBinary(El: TJSBinary);virtual;
  203. Function IsEmptyStatement(El: TJSElement): boolean;
  204. Function HasLineEnding(El: TJSElement): boolean;
  205. Public
  206. Function EscapeString(const S: TJSString; Quote: TJSEscapeQuote = jseqDouble): TJSString;
  207. Constructor Create(AWriter : TTextWriter);
  208. {$ifdef HasFileWriter}
  209. Constructor Create(Const AFileName : String);
  210. {$endif}
  211. Destructor Destroy; override;
  212. Procedure WriteJS(El : TJSElement);
  213. Procedure Indent;
  214. Procedure Undent;
  215. Property Writer : TTextWriter Read FWriter;
  216. Property Options : TWriteOptions Read FOptions Write SetOptions;
  217. Property IndentSize : Byte Read FIndentSize Write FIndentSize;
  218. Property UseUTF8 : Boolean Read GetUseUTF8;
  219. Property LastChar: WideChar read FLastChar;
  220. Property SkipCurlyBrackets : Boolean read FSkipCurlyBrackets write FSkipCurlyBrackets;
  221. Property SkipRoundBrackets : Boolean read FSkipRoundBrackets write FSkipRoundBrackets;
  222. end;
  223. EJSWriter = Class(Exception);
  224. {$ifdef FPC_HAS_CPSTRING}
  225. Function UTF16ToUTF8(const S: UnicodeString): string;
  226. {$endif}
  227. Function QuoteJSString(const S: TJSString; Quote: TJSChar = #0): TJSString;
  228. implementation
  229. Resourcestring
  230. SErrUnknownJSClass = 'Unknown javascript element class : %s';
  231. SErrNilNode = 'Nil node in Javascript';
  232. {$ifdef FPC_HAS_CPSTRING}
  233. function HexDump(p: PChar; Count: integer): string;
  234. var
  235. i: Integer;
  236. begin
  237. Result:='';
  238. for i:=0 to Count-1 do
  239. Result:=Result+HexStr(ord(p[i]),2);
  240. end;
  241. function UTF16ToUTF8(const S: UnicodeString): string;
  242. begin
  243. Result:=UTF8Encode(S);
  244. // prevent UTF8 codepage appear in the strings - we don't need codepage
  245. // conversion magic
  246. SetCodePage(RawByteString(Result), CP_ACP, False);
  247. end;
  248. {$endif}
  249. function QuoteJSString(const S: TJSString; Quote: TJSChar): TJSString;
  250. var
  251. i, j, Count: Integer;
  252. begin
  253. if Quote=#0 then
  254. begin
  255. if Pos('"',S)>0 then
  256. Quote:=''''
  257. else
  258. Quote:='"';
  259. end;
  260. Result := '' + Quote;
  261. Count := length(S);
  262. i := 0;
  263. j := 0;
  264. while i < Count do
  265. begin
  266. inc(i);
  267. if S[i] = Quote then
  268. begin
  269. Result := Result + copy(S, 1 + j, i - j) + Quote;
  270. j := i;
  271. end;
  272. end;
  273. if i <> j then
  274. Result := Result + copy(S, 1 + j, i - j);
  275. Result := Result + Quote;
  276. end;
  277. { TBufferWriter }
  278. function TBufferWriter.GetBufferLength: Integer;
  279. begin
  280. Result:=FBufPos;
  281. end;
  282. function TBufferWriter.GetAsString: TJSWriterString;
  283. begin
  284. {$ifdef pas2js}
  285. if FBufPos<length(FBuffer) then
  286. TJSArray(FBuffer).Length:=FBufPos;
  287. Result:=TJSArray(FBuffer).join('');
  288. {$else}
  289. Result:='';
  290. SetLength(Result,BufferLength);
  291. if (BufferLength>0) then
  292. Move(FBuffer[0],Result[1],BufferLength);
  293. {$endif}
  294. end;
  295. {$ifdef fpc}
  296. function TBufferWriter.GetBuffer: Pointer;
  297. begin
  298. Result:=Pointer(FBuffer);
  299. end;
  300. {$endif}
  301. function TBufferWriter.GetCapacity: Cardinal;
  302. begin
  303. Result:=Length(FBuffer);
  304. end;
  305. {$ifdef FPC_HAS_CPSTRING}
  306. function TBufferWriter.GetUnicodeString: UnicodeString;
  307. Var
  308. SL : Integer;
  309. begin
  310. SL:=BufferLength div SizeOf(UnicodeChar); // Silently ignores last byte
  311. Result:='';
  312. SetLength(Result,SL);
  313. if (SL>0) then
  314. Move(FBuffer[0],Result[1],SL*SizeOf(UnicodeChar));
  315. end;
  316. {$endif}
  317. procedure TBufferWriter.SetAsString(const AValue: TJSWriterString);
  318. begin
  319. {$ifdef pas2js}
  320. SetLength(FBuffer,0);
  321. FCapacity:=0;
  322. {$endif}
  323. FBufPos:=0;
  324. DoWrite(AValue);
  325. end;
  326. procedure TBufferWriter.SetCapacity(AValue: Cardinal);
  327. begin
  328. if FCapacity=AValue then Exit;
  329. SetLength(FBuffer,AValue);
  330. if (FBufPos>Capacity) then
  331. FBufPos:=Capacity;
  332. end;
  333. function TBufferWriter.DoWrite(const S: TJSWriterString): integer;
  334. {$ifdef pas2js}
  335. begin
  336. Result:=Length(S)*2;
  337. if Result=0 then exit;
  338. TJSArray(FBuffer).push(S);
  339. inc(FBufPos);
  340. FCapacity:=FBufPos;
  341. end;
  342. {$else}
  343. Var
  344. DesLen,MinLen : Integer;
  345. begin
  346. Result:=Length(S)*SizeOf(TJSWriterChar);
  347. if Result=0 then exit;
  348. MinLen:=Result+integer(FBufPos);
  349. If (MinLen>integer(Capacity)) then
  350. begin
  351. DesLen:=(FCapacity*3) div 2;
  352. if DesLen>MinLen then
  353. MinLen:=DesLen;
  354. Capacity:=MinLen;
  355. end;
  356. Move(S[1],FBuffer[FBufPos],Result);
  357. FBufPos:=integer(FBufPos)+Result;
  358. end;
  359. {$endif}
  360. {$ifdef FPC_HAS_CPSTRING}
  361. function TBufferWriter.DoWrite(const S: UnicodeString): integer;
  362. Var
  363. DesLen,MinLen : Integer;
  364. begin
  365. Result:=Length(S)*SizeOf(UnicodeChar);
  366. if Result=0 then exit;
  367. MinLen:=Result+integer(FBufPos);
  368. If (MinLen>integer(Capacity)) then
  369. begin
  370. DesLen:=(FCapacity*3) div 2;
  371. if DesLen>MinLen then
  372. MinLen:=DesLen;
  373. Capacity:=MinLen;
  374. end;
  375. Move(S[1],FBuffer[FBufPos],Result);
  376. FBufPos:=integer(FBufPos)+Result;
  377. end;
  378. {$endif}
  379. constructor TBufferWriter.Create(const ACapacity: Cardinal);
  380. begin
  381. inherited Create;
  382. Capacity:=ACapacity;
  383. end;
  384. {$ifdef fpc}
  385. procedure TBufferWriter.SaveToFile(const AFileName: String);
  386. Var
  387. F : File;
  388. begin
  389. Assign(F,AFileName);
  390. Rewrite(F,1);
  391. try
  392. BlockWrite(F,FBuffer[0],FBufPos);
  393. finally
  394. Close(F);
  395. end;
  396. end;
  397. {$endif}
  398. { TJSWriter }
  399. {AllowWriteln}
  400. procedure TJSWriter.SetOptions(AValue: TWriteOptions);
  401. begin
  402. if FOptions=AValue then Exit;
  403. FOptions:=AValue;
  404. If woTabIndent in FOptions then
  405. FIndentChar:=#9
  406. else
  407. FIndentChar:=' ';
  408. end;
  409. function TJSWriter.GetUseUTF8: Boolean;
  410. begin
  411. Result:={$ifdef FPC_HAS_CPSTRING}(woUseUTF8 in Options){$else}false{$endif};
  412. end;
  413. procedure TJSWriter.Error(const Msg: TJSWriterString);
  414. begin
  415. Raise EJSWriter.Create(Msg);
  416. end;
  417. procedure TJSWriter.Error(const Fmt: TJSWriterString;
  418. Args: array of const);
  419. begin
  420. Raise EJSWriter.CreateFmt(Fmt,Args);
  421. end;
  422. procedure TJSWriter.WriteIndent;
  423. begin
  424. If (FLinePos=0) and (FCurIndent>0) then
  425. begin
  426. FLinePos:=Writer.Write(StringOfChar(FIndentChar,FCurIndent));
  427. FLastChar:=WideChar(FIndentChar);
  428. end;
  429. end;
  430. procedure TJSWriter.Indent;
  431. begin
  432. Inc(FCurIndent,FIndentSIze);
  433. end;
  434. procedure TJSWriter.Undent;
  435. begin
  436. if (FCurIndent>=FIndentSIze) then
  437. Dec(FCurIndent,FIndentSIze)
  438. else
  439. FCurIndent:=0;
  440. end;
  441. {$ifdef FPC_HAS_CPSTRING}
  442. procedure TJSWriter.Write(const U: UnicodeString);
  443. Var
  444. S : String;
  445. begin
  446. //system.writeln('TJSWriter.Write unicodestring=',U);
  447. WriteIndent;
  448. if UseUTF8 then
  449. begin
  450. S:=UTF16ToUTF8(U);
  451. if S='' then exit;
  452. FLinePos:=FLinePos+Writer.Write(S);
  453. FLastChar:=WideChar(S[length(S)]);
  454. end
  455. else if U<>'' then
  456. begin
  457. FLinePos:=FLinePos+Writer.Write(U);
  458. FLastChar:=U[length(U)];
  459. end;
  460. end;
  461. {$endif}
  462. procedure TJSWriter.Write(const S: TJSWriterString);
  463. begin
  464. //system.writeln('TJSWriter.Write TJSWriterString=',S);
  465. {$ifdef FPC_HAS_CPSTRING}
  466. if Not (woUseUTF8 in Options) then
  467. Write(UnicodeString(S))
  468. else
  469. {$endif}
  470. begin
  471. WriteIndent;
  472. if s='' then exit;
  473. FLinePos:=FLinePos+Writer.Write(S);
  474. FLastChar:=WideChar(S[length(S)]);
  475. end;
  476. end;
  477. procedure TJSWriter.WriteLn(const S: TJSWriterString);
  478. begin
  479. {$ifdef FPC_HAS_CPSTRING}
  480. if Not (woUseUTF8 in Options) then
  481. Writeln(UnicodeString(S))
  482. else
  483. {$endif}
  484. begin
  485. WriteIndent;
  486. Writer.WriteLn(S);
  487. FLastChar:=WideChar(#10);
  488. FLinePos:=0;
  489. end;
  490. end;
  491. {$ifdef FPC_HAS_CPSTRING}
  492. procedure TJSWriter.WriteLn(const U: UnicodeString);
  493. Var
  494. S : String;
  495. begin
  496. if UseUTF8 then
  497. begin
  498. S:=UTF16ToUTF8(U);
  499. Writeln(S);
  500. end
  501. else
  502. begin
  503. WriteIndent;
  504. FLinePos:=FLinePos+Writer.Write(U);
  505. Writer.WriteLn('');
  506. FLastChar:=WideChar(#10);
  507. FLinePos:=0;
  508. end;
  509. end;
  510. {$endif}
  511. function TJSWriter.EscapeString(const S: TJSString; Quote: TJSEscapeQuote
  512. ): TJSString;
  513. Var
  514. I,J,L : Integer;
  515. R: TJSString;
  516. c: WideChar;
  517. begin
  518. //system.writeln('TJSWriter.EscapeString "',S,'"');
  519. I:=1;
  520. J:=1;
  521. R:='';
  522. L:=Length(S);
  523. While I<=L do
  524. begin
  525. c:=S[I];
  526. if (c in [#0..#31,'"','''','/','\'])
  527. or (c>=#$ff00) or ((c>=#$D800) and (c<=#$DFFF)) then
  528. begin
  529. R:=R+Copy(S,J,I-J);
  530. Case c of
  531. '\' : R:=R+'\\';
  532. '/' : R:=R+'\/';
  533. '"' : if Quote=jseqSingle then R:=R+'"' else R:=R+'\"';
  534. '''': if Quote=jseqDouble then R:=R+'''' else R:=R+'\''';
  535. #0..#7,#11,#14..#31: R:=R+'\x'+TJSString(hexStr(ord(c),2));
  536. #8 : R:=R+'\b';
  537. #9 : R:=R+'\t';
  538. #10 : R:=R+'\n';
  539. #12 : R:=R+'\f';
  540. #13 : R:=R+'\r';
  541. #$D800..#$DFFF:
  542. begin
  543. if (I<L) then
  544. begin
  545. c:=S[I+1];
  546. if (c>=#$D000) and (c<=#$DFFF) then
  547. begin
  548. inc(I,2); // surrogate, two char codepoint
  549. continue;
  550. end;
  551. // invalid UTF-16, cannot be encoded as UTF-8 -> encode as hex
  552. R:=R+'\u'+TJSString(HexStr(ord(S[i]),4));
  553. end
  554. else
  555. // invalid UTF-16 at end of string, cannot be encoded as UTF-8 -> encode as hex
  556. R:=R+'\u'+TJSString(HexStr(ord(c),4));
  557. end;
  558. #$FF00..#$FFFF: R:=R+'\u'+TJSString(HexStr(ord(c),4));
  559. end;
  560. J:=I+1;
  561. end;
  562. Inc(I);
  563. end;
  564. R:=R+Copy(S,J,I-1);
  565. Result:=R;
  566. //system.writeln('TJSWriter.EscapeString Result="',Result,'"');
  567. end;
  568. procedure TJSWriter.WriteValue(V: TJSValue);
  569. const
  570. TabWidth = 4;
  571. function GetLineIndent(const S: TJSString; var p: integer): integer;
  572. var
  573. h, l: integer;
  574. begin
  575. h:=p;
  576. l:=length(S);
  577. Result:=0;
  578. while h<=l do
  579. begin
  580. case S[h] of
  581. #9: Result:=Result+(TabWidth-Result mod TabWidth);
  582. ' ': inc(Result);
  583. else break;
  584. end;
  585. inc(h);
  586. end;
  587. p:=h;
  588. end;
  589. function SkipToNextLineEnd(const S: TJSString; p: integer): integer;
  590. var
  591. l: SizeInt;
  592. begin
  593. l:=length(S);
  594. while (p<=l) and not (S[p] in [#10,#13]) do inc(p);
  595. Result:=p;
  596. end;
  597. function SkipToNextLineStart(const S: TJSString; p: integer): integer;
  598. var
  599. l: Integer;
  600. begin
  601. l:=length(S);
  602. while p<=l do
  603. begin
  604. case S[p] of
  605. #10,#13:
  606. begin
  607. if (p<l) and (S[p+1] in [#10,#13]) and (S[p]<>S[p+1]) then
  608. inc(p,2)
  609. else
  610. inc(p);
  611. break;
  612. end
  613. else inc(p);
  614. end;
  615. end;
  616. Result:=p;
  617. end;
  618. Var
  619. S , S2: String;
  620. JS: TJSString;
  621. p, StartP: Integer;
  622. MinIndent, CurLineIndent, j, Exp, Code: Integer;
  623. i: SizeInt;
  624. D, AsNumber: TJSNumber;
  625. begin
  626. if V.CustomValue<>'' then
  627. begin
  628. JS:=V.CustomValue;
  629. if JS='' then exit;
  630. p:=SkipToNextLineStart(JS,1);
  631. if p>length(JS) then
  632. begin
  633. // simple value
  634. Write(JS);
  635. exit;
  636. end;
  637. // multi line value
  638. // find minimum indent
  639. MinIndent:=-1;
  640. repeat
  641. CurLineIndent:=GetLineIndent(JS,p);
  642. if (MinIndent<0) or (MinIndent>CurLineIndent) then
  643. MinIndent:=CurLineIndent;
  644. p:=SkipToNextLineStart(JS,p);
  645. until p>length(JS);
  646. // write value lines indented
  647. p:=1;
  648. GetLineIndent(JS,p); // the first line is already indented, skip
  649. repeat
  650. StartP:=p;
  651. p:=SkipToNextLineEnd(JS,StartP);
  652. Write(copy(JS,StartP,p-StartP));
  653. if p>length(JS) then break;
  654. Write(sLineBreak);
  655. p:=SkipToNextLineStart(JS,p);
  656. CurLineIndent:=GetLineIndent(JS,p);
  657. Write(StringOfChar(FIndentChar,FCurIndent+CurLineIndent-MinIndent));
  658. until false;
  659. exit;
  660. end;
  661. Case V.ValueType of
  662. jstUNDEFINED : S:='undefined';
  663. jstNull : s:='null';
  664. jstBoolean : if V.AsBoolean then s:='true' else s:='false';
  665. jstString :
  666. begin
  667. JS:=V.AsString;
  668. if Pos('"',JS)>0 then
  669. JS:=''''+EscapeString(JS,jseqSingle)+''''
  670. else
  671. JS:='"'+EscapeString(JS,jseqDouble)+'"';
  672. Write(JS);
  673. exit;
  674. end;
  675. jstNumber :
  676. begin
  677. AsNumber:=V.AsNumber;
  678. if (Frac(AsNumber)=0)
  679. and (AsNumber>=double(MinSafeIntDouble))
  680. and (AsNumber<=double(MaxSafeIntDouble)) then
  681. begin
  682. Str(Round(AsNumber),S);
  683. end
  684. else
  685. begin
  686. Str(AsNumber,S);
  687. if S[1]=' ' then Delete(S,1,1);
  688. i:=Pos('E',S);
  689. if (i>2) then
  690. begin
  691. j:=i-2;
  692. case s[j] of
  693. '0':
  694. begin
  695. // check for 1.2340000000000001E...
  696. while (j>1) and (s[j]='0') do dec(j);
  697. if s[j]='.' then inc(j);
  698. S2:=LeftStr(S,j)+copy(S,i,length(S));
  699. val(S2,D,Code);
  700. if (Code=0) and (D=AsNumber) then
  701. S:=S2;
  702. end;
  703. '9':
  704. begin
  705. // check for 1.234999999999991E...
  706. while (j>1) and (s[j]='9') do dec(j);
  707. // cut '99999'
  708. S2:=LeftStr(S,j)+copy(S,i,length(S));
  709. if S[j]='.' then
  710. Insert('0',S2,j+1);
  711. // increment, e.g. 1.2999 -> 1.3
  712. repeat
  713. case S2[j] of
  714. '0'..'8':
  715. begin
  716. S2[j]:=chr(ord(S2[j])+1);
  717. break;
  718. end;
  719. '9':
  720. S2[j]:='0';
  721. '.': ;
  722. end;
  723. dec(j);
  724. if (j=0) or not (S2[j] in ['0'..'9','.']) then
  725. begin
  726. // e.g. -9.999 became 0.0
  727. val(copy(S,i+1,length(S)),Exp,Code);
  728. if Code=0 then
  729. begin
  730. S2:='1E'+IntToStr(Exp+1);
  731. if S[1]='-' then
  732. S2:='-'+S2;
  733. end;
  734. break;
  735. end;
  736. until false;
  737. val(S2,D,Code);
  738. if (Code=0) and (D=AsNumber) then
  739. S:=S2;
  740. end;
  741. else
  742. if s[i-1]='0' then
  743. begin
  744. // 1.2340E...
  745. S2:=LeftStr(S,i-2)+copy(S,i,length(S));
  746. val(S2,D,Code);
  747. if (Code=0) and (D=AsNumber) then
  748. S:=S2;
  749. end;
  750. end;
  751. end;
  752. // chomp default exponent E+000
  753. i:=Pos('E',S);
  754. if i>0 then
  755. begin
  756. val(copy(S,i+1,length(S)),Exp,Code);
  757. if Code=0 then
  758. begin
  759. if Exp=0 then
  760. // 1.1E+000 -> 1.1
  761. Delete(S,i,length(S))
  762. else if (Exp>=-6) and (Exp<=6) then
  763. begin
  764. // small exponent -> use notation without E
  765. Delete(S,i,length(S));
  766. j:=Pos('.',S);
  767. if j>0 then
  768. Delete(S,j,1)
  769. else
  770. begin
  771. j:=1;
  772. while not (S[j] in ['0'..'9']) do inc(j);
  773. end;
  774. if Exp<0 then
  775. begin
  776. // e.g. -1.2 E-1
  777. while Exp<0 do
  778. begin
  779. if (j>1) and (S[j-1] in ['0'..'9']) then
  780. dec(j)
  781. else
  782. Insert('0',S,j);
  783. inc(Exp);
  784. end;
  785. Insert('.',S,j);
  786. if (j=1) or not (S[j-1] in ['0'..'9']) then
  787. Insert('0',S,j);
  788. if S[length(S)]='0' then
  789. Delete(S,length(S),1);
  790. end
  791. else
  792. begin
  793. // e.g. -1.2 E1
  794. while Exp>0 do
  795. begin
  796. if (j<=length(S)) and (S[j] in ['0'..'9']) then
  797. inc(j)
  798. else
  799. Insert('0',S,j);
  800. dec(Exp);
  801. end;
  802. if j<=length(S) then
  803. Insert('.',S,j);
  804. end;
  805. end
  806. else
  807. begin
  808. // e.g. 1.1E+0010 -> 1.1E10
  809. S:=LeftStr(S,i)+IntToStr(Exp);
  810. if (i >= 4) and (s[i-1] = '0') and (s[i-2] = '.') then
  811. // e.g. 1.0E22 -> 1E22
  812. Delete(S, i-2, 2);
  813. end
  814. end;
  815. end;
  816. end;
  817. end;
  818. jstObject : ;
  819. jstReference : ;
  820. jstCompletion : ;
  821. end;
  822. if S='' then exit;
  823. case S[1] of
  824. '+': if FLastChar='+' then Write(' ');
  825. '-': if FLastChar='-' then Write(' ');
  826. end;
  827. Write(S);
  828. end;
  829. constructor TJSWriter.Create(AWriter: TTextWriter);
  830. begin
  831. FWriter:=AWriter;
  832. FIndentChar:=' ';
  833. FOptions:=[{$ifdef FPC_HAS_CPSTRING}woUseUTF8{$endif}];
  834. end;
  835. {$ifdef HasFileWriter}
  836. constructor TJSWriter.Create(const AFileName: String);
  837. begin
  838. Create(TFileWriter.Create(AFileName));
  839. FFreeWriter:=True;
  840. end;
  841. {$endif}
  842. destructor TJSWriter.Destroy;
  843. begin
  844. If FFreeWriter then
  845. begin
  846. FWriter.Free;
  847. FWriter:=Nil;
  848. end;
  849. inherited Destroy;
  850. end;
  851. procedure TJSWriter.WriteFuncDef(FD: TJSFuncDef);
  852. Var
  853. C : Boolean;
  854. I : Integer;
  855. A, LastEl: TJSElement;
  856. begin
  857. LastEl:=Writer.CurElement;
  858. C:=(woCompact in Options);
  859. if fd.IsAsync then
  860. Write('async ');
  861. Write('function ');
  862. If (FD.Name<>'') then
  863. Write(FD.Name);
  864. Write('(');
  865. if Assigned(FD.Params) then
  866. For I:=0 to FD.Params.Count-1 do
  867. begin
  868. write(FD.Params[i]);
  869. if I<FD.Params.Count-1 then
  870. if C then Write(',') else Write (', ');
  871. end;
  872. Write(') {');
  873. if Not (C or FD.IsEmpty) then
  874. begin
  875. Writeln('');
  876. Indent;
  877. end;
  878. if Assigned(FD.Body) then
  879. begin
  880. FSkipCurlyBrackets:=True;
  881. //writeln('TJSWriter.WriteFuncDef '+FD.Body.ClassName);
  882. WriteJS(FD.Body);
  883. A:=FD.Body.A;
  884. If (Assigned(A))
  885. and (not (A is TJSStatementList))
  886. and (not (A is TJSSourceElements))
  887. and (not (A is TJSEmptyBlockStatement))
  888. then
  889. begin
  890. if FLastChar<>';' then
  891. Write(';');
  892. if C then
  893. Write(' ')
  894. else
  895. Writeln('');
  896. end;
  897. end;
  898. Writer.CurElement:=LastEl;
  899. if C then
  900. Write('}')
  901. else
  902. begin
  903. Undent;
  904. Write('}'); // do not writeln
  905. end;
  906. end;
  907. procedure TJSWriter.WriteEmptyBlockStatement(El: TJSEmptyBlockStatement);
  908. begin
  909. if El=nil then ;
  910. if woCompact in Options then
  911. Write('{}')
  912. else
  913. begin
  914. Writeln('{');
  915. Write('}');
  916. end;
  917. end;
  918. procedure TJSWriter.WriteEmptyStatement(El: TJSEmptyStatement);
  919. begin
  920. if El=nil then ;
  921. if woEmptyStatementAsComment in Options then
  922. Write('/* Empty statement */')
  923. end;
  924. procedure TJSWriter.WriteRegularExpressionLiteral(
  925. El: TJSRegularExpressionLiteral);
  926. begin
  927. Write('/');
  928. Write(EscapeString(El.Pattern.AsString,jseqBoth));
  929. Write('/');
  930. If Assigned(El.PatternFlags) then
  931. Write(EscapeString(El.PatternFlags.AsString,jseqBoth));
  932. end;
  933. procedure TJSWriter.WriteLiteral(El: TJSLiteral);
  934. begin
  935. WriteValue(El.Value);
  936. end;
  937. procedure TJSWriter.WritePrimaryExpression(El: TJSPrimaryExpression);
  938. begin
  939. if El is TJSPrimaryExpressionThis then
  940. Write('this')
  941. else if El is TJSPrimaryExpressionIdent then
  942. Write(TJSPrimaryExpressionIdent(El).Name)
  943. else
  944. Error(SErrUnknownJSClass,[El.ClassName]);
  945. end;
  946. procedure TJSWriter.WriteArrayLiteral(El: TJSArrayLiteral);
  947. type
  948. BracketString = string{$ifdef fpc}[2]{$endif};
  949. Var
  950. Chars : Array[Boolean] of BracketString = ('[]','()');
  951. Var
  952. i,C : Integer;
  953. isArgs,WC , MultiLine: Boolean;
  954. BC : BracketString;
  955. begin
  956. isArgs:=El is TJSArguments;
  957. BC:=Chars[isArgs];
  958. C:=El.Elements.Count-1;
  959. if C=-1 then
  960. begin
  961. Write(BC);
  962. Exit;
  963. end;
  964. WC:=(woCompact in Options) or
  965. ((Not isArgs) and (woCompactArrayLiterals in Options)) or
  966. (isArgs and (woCompactArguments in Options)) ;
  967. MultiLine:=(not WC) and (C>4);
  968. if MultiLine then
  969. begin
  970. Writeln(BC[1]);
  971. Indent;
  972. end
  973. else
  974. Write(BC[1]);
  975. For I:=0 to C do
  976. begin
  977. FSkipRoundBrackets:=true;
  978. WriteJS(El.Elements[i].Expr);
  979. if I<C then
  980. if WC then
  981. Write(',')
  982. else if MultiLine then
  983. Writeln(',')
  984. else
  985. Write(', ');
  986. end;
  987. if MultiLine then
  988. begin
  989. Writeln('');
  990. Undent;
  991. end;
  992. Writer.CurElement:=El;
  993. Write(BC[2]);
  994. end;
  995. procedure TJSWriter.WriteObjectLiteral(El: TJSObjectLiteral);
  996. Var
  997. i,C : Integer;
  998. QE,WC : Boolean;
  999. S : TJSString;
  1000. Prop: TJSObjectLiteralElement;
  1001. begin
  1002. C:=El.Elements.Count-1;
  1003. QE:=(woQuoteElementNames in Options);
  1004. if C=-1 then
  1005. begin
  1006. Write('{}');
  1007. Exit;
  1008. end;
  1009. WC:=(woCompact in Options) or (woCompactObjectLiterals in Options);
  1010. if WC then
  1011. Write('{')
  1012. else
  1013. begin
  1014. Writeln('{');
  1015. Indent;
  1016. end;
  1017. For I:=0 to C do
  1018. begin
  1019. Prop:=El.Elements[i];
  1020. Writer.CurElement:=Prop.Expr;
  1021. S:=Prop.Name;
  1022. if QE or not IsValidJSIdentifier(S) then
  1023. begin
  1024. if (length(S)>1)
  1025. and (((S[1]='"') and (S[length(S)]='"'))
  1026. or ((S[1]='''') and (S[length(S)]=''''))) then
  1027. // already quoted
  1028. else
  1029. S:=QuoteJSString(s);
  1030. end;
  1031. Write(S+': ');
  1032. Indent;
  1033. FSkipRoundBrackets:=true;
  1034. WriteJS(Prop.Expr);
  1035. if I<C then
  1036. if WC then Write(', ') else Writeln(',');
  1037. Undent;
  1038. end;
  1039. FSkipRoundBrackets:=false;
  1040. if not WC then
  1041. begin
  1042. Writeln('');
  1043. Undent;
  1044. end;
  1045. Writer.CurElement:=El;
  1046. Write('}');
  1047. end;
  1048. procedure TJSWriter.WriteMemberExpression(El: TJSMemberExpression);
  1049. var
  1050. MExpr: TJSElement;
  1051. Args: TJSArguments;
  1052. begin
  1053. if El is TJSNewMemberExpression then
  1054. Write('new ');
  1055. MExpr:=El.MExpr;
  1056. if (MExpr is TJSPrimaryExpression)
  1057. or (MExpr is TJSDotMemberExpression)
  1058. or (MExpr is TJSBracketMemberExpression)
  1059. // Note: new requires brackets in this case: new (a())()
  1060. or ((MExpr is TJSCallExpression) and not (El is TJSNewMemberExpression))
  1061. or (MExpr is TJSLiteral) then
  1062. begin
  1063. WriteJS(MExpr);
  1064. Writer.CurElement:=El;
  1065. end
  1066. else
  1067. begin
  1068. Write('(');
  1069. WriteJS(MExpr);
  1070. Writer.CurElement:=El;
  1071. Write(')');
  1072. end;
  1073. if El is TJSDotMemberExpression then
  1074. begin
  1075. Write('.');
  1076. Write(TJSDotMemberExpression(El).Name);
  1077. end
  1078. else if El is TJSBracketMemberExpression then
  1079. begin
  1080. write('[');
  1081. FSkipRoundBrackets:=true;
  1082. WriteJS(TJSBracketMemberExpression(El).Name);
  1083. Writer.CurElement:=El;
  1084. FSkipRoundBrackets:=false;
  1085. write(']');
  1086. end
  1087. else if (El is TJSNewMemberExpression) then
  1088. begin
  1089. Args:=TJSNewMemberExpression(El).Args;
  1090. if Assigned(Args) then
  1091. begin
  1092. Writer.CurElement:=Args;
  1093. WriteArrayLiteral(Args);
  1094. end
  1095. else
  1096. Write('()');
  1097. end;
  1098. end;
  1099. procedure TJSWriter.WriteCallExpression(El: TJSCallExpression);
  1100. begin
  1101. WriteJS(El.Expr);
  1102. if Assigned(El.Args) then
  1103. begin
  1104. Writer.CurElement:=El.Args;
  1105. WriteArrayLiteral(El.Args);
  1106. end
  1107. else
  1108. begin
  1109. Writer.CurElement:=El;
  1110. Write('()');
  1111. end;
  1112. end;
  1113. procedure TJSWriter.WriteUnary(El: TJSUnary);
  1114. Var
  1115. S : String;
  1116. begin
  1117. FSkipRoundBrackets:=false;
  1118. S:=El.PreFixOperator;
  1119. if (S<>'') then
  1120. begin
  1121. case S[1] of
  1122. '+': if FLastChar='+' then Write(' ');
  1123. '-': if FLastChar='-' then Write(' ');
  1124. end;
  1125. Write(S);
  1126. end;
  1127. WriteJS(El.A);
  1128. S:=El.PostFixOperator;
  1129. if (S<>'') then
  1130. begin
  1131. Writer.CurElement:=El;
  1132. case S[1] of
  1133. '+': if FLastChar='+' then Write(' ');
  1134. '-': if FLastChar='-' then Write(' ');
  1135. end;
  1136. Write(S);
  1137. end;
  1138. end;
  1139. procedure TJSWriter.WriteStatementList(El: TJSStatementList);
  1140. Var
  1141. C : Boolean;
  1142. LastEl: TJSElement;
  1143. ElStack: array of TJSElement;
  1144. ElStackIndex: integer;
  1145. procedure WriteNonListEl(CurEl: TJSElement);
  1146. begin
  1147. if IsEmptyStatement(CurEl) then exit;
  1148. if (LastEl<>nil) then
  1149. begin
  1150. if FLastChar<>';' then
  1151. Write(';');
  1152. if C then
  1153. Write(' ')
  1154. else
  1155. Writeln('');
  1156. end;
  1157. WriteJS(CurEl);
  1158. LastEl:=CurEl;
  1159. end;
  1160. procedure Push(CurEl: TJSElement);
  1161. begin
  1162. if CurEl=nil then exit;
  1163. if ElStackIndex=length(ElStack) then
  1164. SetLength(ElStack,ElStackIndex+8);
  1165. ElStack[ElStackIndex]:=CurEl;
  1166. inc(ElStackIndex);
  1167. end;
  1168. function Pop: TJSElement;
  1169. begin
  1170. if ElStackIndex=0 then exit(nil);
  1171. dec(ElStackIndex);
  1172. Result:=ElStack[ElStackIndex];
  1173. end;
  1174. var
  1175. B : Boolean;
  1176. CurEl: TJSElement;
  1177. List: TJSStatementList;
  1178. begin
  1179. //write('TJSWriter.WriteStatementList '+BoolToStr(FSkipCurlyBrackets,true));
  1180. //if El.A<>nil then write(' El.A='+El.A.ClassName) else write(' El.A=nil');
  1181. //if El.B<>nil then write(' El.B='+El.B.ClassName) else write(' El.B=nil');
  1182. //writeln(' ');
  1183. C:=(woCompact in Options);
  1184. B:= Not FSkipCurlyBrackets;
  1185. FSkipCurlyBrackets:=True;
  1186. if B then
  1187. begin
  1188. Write('{');
  1189. Indent;
  1190. if not C then writeln('');
  1191. end;
  1192. // traverse statementlist using a heap stack to avoid large stack depths
  1193. LastEl:=nil;
  1194. ElStackIndex:=0;
  1195. CurEl:=El;
  1196. while CurEl<>nil do
  1197. begin
  1198. if CurEl is TJSStatementList then
  1199. begin
  1200. List:=TJSStatementList(CurEl);
  1201. if List.A is TJSStatementList then
  1202. begin
  1203. Push(List.B);
  1204. CurEl:=List.A;
  1205. end
  1206. else
  1207. begin
  1208. WriteNonListEl(List.A);
  1209. if List.B is TJSStatementList then
  1210. CurEl:=List.B
  1211. else
  1212. begin
  1213. WriteNonListEl(List.B);
  1214. CurEl:=nil;
  1215. end;
  1216. end;
  1217. end
  1218. else
  1219. begin
  1220. WriteNonListEl(CurEl);
  1221. CurEl:=nil;
  1222. end;
  1223. if CurEl=nil then
  1224. CurEl:=Pop;
  1225. end;
  1226. if (LastEl<>nil) and not C then
  1227. if FLastChar=';' then
  1228. writeln('')
  1229. else
  1230. writeln(';');
  1231. if B then
  1232. begin
  1233. Undent;
  1234. Writer.CurElement:=El;
  1235. Write('}'); // do not writeln
  1236. end;
  1237. end;
  1238. procedure TJSWriter.WriteWithStatement(El: TJSWithStatement);
  1239. begin
  1240. Write('with (');
  1241. FSkipRoundBrackets:=true;
  1242. WriteJS(El.A);
  1243. FSkipRoundBrackets:=false;
  1244. Writer.CurElement:=El;
  1245. if (woCompact in Options) then
  1246. Write(') ')
  1247. else
  1248. WriteLn(')');
  1249. Indent;
  1250. WriteJS(El.B);
  1251. Undent;
  1252. end;
  1253. procedure TJSWriter.WriteVarDeclarationList(El: TJSVariableDeclarationList);
  1254. begin
  1255. WriteJS(El.A);
  1256. If Assigned(El.B) then
  1257. begin
  1258. Write(', ');
  1259. WriteJS(El.B);
  1260. end;
  1261. end;
  1262. procedure TJSWriter.WriteBinary(El: TJSBinary);
  1263. var
  1264. ElC: TClass;
  1265. S : String;
  1266. procedure WriteRight(Bin: TJSBinary);
  1267. begin
  1268. FSkipRoundBrackets:=(Bin.B.ClassType=ElC)
  1269. and ((ElC=TJSLogicalOrExpression)
  1270. or (ElC=TJSLogicalAndExpression));
  1271. Write(S);
  1272. WriteJS(Bin.B);
  1273. Writer.CurElement:=Bin;
  1274. end;
  1275. Var
  1276. AllowCompact, WithBrackets: Boolean;
  1277. Left: TJSElement;
  1278. SubBin: TJSBinaryExpression;
  1279. Binaries: TJSElementArray;
  1280. BinariesCnt: integer;
  1281. begin
  1282. {$IFDEF VerboseJSWriter}
  1283. System.writeln('TJSWriter.WriteBinary SkipRoundBrackets=',FSkipRoundBrackets);
  1284. {$ENDIF}
  1285. WithBrackets:=not FSkipRoundBrackets;
  1286. if WithBrackets then
  1287. Write('(');
  1288. FSkipRoundBrackets:=false;
  1289. ElC:=El.ClassType;
  1290. Left:=El.A;
  1291. AllowCompact:=False;
  1292. S:='';
  1293. if (El is TJSBinaryExpression) then
  1294. begin
  1295. S:=TJSBinaryExpression(El).OperatorString;
  1296. AllowCompact:=TJSBinaryExpression(El).AllowCompact;
  1297. end;
  1298. If Not (AllowCompact and (woCompact in Options)) then
  1299. begin
  1300. if El is TJSCommaExpression then
  1301. S:=S+' '
  1302. else
  1303. S:=' '+S+' ';
  1304. end;
  1305. if (Left is TJSBinaryExpression)
  1306. and (Left.ClassType=ElC)
  1307. and ((ElC=TJSLogicalOrExpression)
  1308. or (ElC=TJSLogicalAndExpression)
  1309. or (ElC=TJSBitwiseAndExpression)
  1310. or (ElC=TJSBitwiseOrExpression)
  1311. or (ElC=TJSBitwiseXOrExpression)
  1312. or (ElC=TJSAdditiveExpressionPlus)
  1313. or (ElC=TJSAdditiveExpressionMinus)
  1314. or (ElC=TJSMultiplicativeExpressionMul)) then
  1315. begin
  1316. // handle left handed multi add without stack
  1317. SetLength(Binaries{%H-},8);
  1318. BinariesCnt:=0;
  1319. while Left is TJSBinaryExpression do
  1320. begin
  1321. SubBin:=TJSBinaryExpression(Left);
  1322. if SubBin.ClassType<>ElC then break;
  1323. if BinariesCnt=length(Binaries) then
  1324. SetLength(Binaries,BinariesCnt*2);
  1325. Binaries[BinariesCnt]:=SubBin;
  1326. inc(BinariesCnt);
  1327. Left:=SubBin.A;
  1328. end;
  1329. WriteJS(Left);
  1330. Writer.CurElement:=El;
  1331. while BinariesCnt>0 do
  1332. begin
  1333. dec(BinariesCnt);
  1334. WriteRight(TJSBinaryExpression(Binaries[BinariesCnt]));
  1335. end;
  1336. end
  1337. else
  1338. begin;
  1339. WriteJS(Left);
  1340. Writer.CurElement:=El;
  1341. end;
  1342. WriteRight(El);
  1343. if WithBrackets then
  1344. Write(')');
  1345. end;
  1346. function TJSWriter.IsEmptyStatement(El: TJSElement): boolean;
  1347. begin
  1348. if (El=nil) then
  1349. exit(true);
  1350. if (El.ClassType=TJSEmptyStatement) and not (woEmptyStatementAsComment in Options) then
  1351. exit(true);
  1352. Result:=false;
  1353. end;
  1354. function TJSWriter.HasLineEnding(El: TJSElement): boolean;
  1355. begin
  1356. if El<>nil then
  1357. begin
  1358. if (El.ClassType=TJSStatementList) or (El.ClassType=TJSSourceElements) then
  1359. exit(true);
  1360. end;
  1361. Result:=false;
  1362. end;
  1363. procedure TJSWriter.WriteConditionalExpression(El: TJSConditionalExpression);
  1364. var
  1365. NeedBrackets: Boolean;
  1366. begin
  1367. NeedBrackets:=true;
  1368. if NeedBrackets then
  1369. begin
  1370. write('(');
  1371. FSkipRoundBrackets:=true;
  1372. end;
  1373. WriteJS(El.A);
  1374. write(' ? ');
  1375. if El.B<>nil then
  1376. WriteJS(El.B);
  1377. write(' : ');
  1378. if El.C<>nil then
  1379. WriteJS(El.C);
  1380. if NeedBrackets then
  1381. write(')');
  1382. end;
  1383. procedure TJSWriter.WriteAssignStatement(El: TJSAssignStatement);
  1384. Var
  1385. S : String;
  1386. begin
  1387. WriteJS(El.LHS);
  1388. Writer.CurElement:=El;
  1389. S:=El.OperatorString;
  1390. If Not (woCompact in Options) then
  1391. S:=' '+S+' ';
  1392. Write(S);
  1393. FSkipRoundBrackets:=true;
  1394. WriteJS(El.Expr);
  1395. FSkipRoundBrackets:=false;
  1396. end;
  1397. procedure TJSWriter.WriteVarDeclaration(El: TJSVarDeclaration);
  1398. begin
  1399. Write(El.Name);
  1400. if Assigned(El.Init) then
  1401. begin
  1402. Write(' = ');
  1403. FSkipRoundBrackets:=true;
  1404. WriteJS(El.Init);
  1405. FSkipRoundBrackets:=false;
  1406. end;
  1407. end;
  1408. procedure TJSWriter.WriteIfStatement(El: TJSIfStatement);
  1409. var
  1410. HasBTrue, C, HasBFalse, BTrueNeedBrackets: Boolean;
  1411. begin
  1412. C:=woCompact in Options;
  1413. Write('if (');
  1414. FSkipRoundBrackets:=true;
  1415. WriteJS(El.Cond);
  1416. Writer.CurElement:=El;
  1417. FSkipRoundBrackets:=false;
  1418. Write(')');
  1419. If Not C then
  1420. Write(' ');
  1421. HasBTrue:=not IsEmptyStatement(El.BTrue);
  1422. HasBFalse:=not IsEmptyStatement(El.BFalse);
  1423. if HasBTrue then
  1424. begin
  1425. // Note: the 'else' needs {} in front
  1426. BTrueNeedBrackets:=HasBFalse and not (El.BTrue is TJSStatementList)
  1427. and not (El.BTrue is TJSEmptyBlockStatement);
  1428. if BTrueNeedBrackets then
  1429. if C then
  1430. Write('{')
  1431. else
  1432. begin
  1433. Writeln('{');
  1434. Indent;
  1435. end;
  1436. WriteJS(El.BTrue);
  1437. if BTrueNeedBrackets then
  1438. if C then
  1439. Write('}')
  1440. else
  1441. begin
  1442. Undent;
  1443. Writeln('}');
  1444. end;
  1445. end;
  1446. if HasBFalse then
  1447. begin
  1448. Writer.CurElement:=El.BFalse;
  1449. if not HasBTrue then
  1450. begin
  1451. if C then
  1452. Write('{}')
  1453. else
  1454. Writeln('{}');
  1455. end
  1456. else
  1457. Write(' ');
  1458. Write('else ');
  1459. WriteJS(El.BFalse)
  1460. end
  1461. else
  1462. Writer.CurElement:=El;
  1463. end;
  1464. procedure TJSWriter.WriteForInStatement(El: TJSForInStatement);
  1465. begin
  1466. Write('for (');
  1467. if Assigned(El.LHS) then
  1468. begin
  1469. WriteJS(El.LHS);
  1470. Writer.CurElement:=El;
  1471. end;
  1472. Write(' in ');
  1473. if Assigned(El.List) then
  1474. begin
  1475. WriteJS(El.List);
  1476. Writer.CurElement:=El;
  1477. end;
  1478. Write(') ');
  1479. if Assigned(El.Body) then
  1480. WriteJS(El.Body);
  1481. end;
  1482. procedure TJSWriter.WriteForStatement(El: TJSForStatement);
  1483. begin
  1484. Write('for (');
  1485. if Assigned(El.Init) then
  1486. WriteJS(El.Init);
  1487. Write('; ');
  1488. if Assigned(El.Cond) then
  1489. begin
  1490. FSkipRoundBrackets:=true;
  1491. WriteJS(El.Cond);
  1492. FSkipRoundBrackets:=false;
  1493. end;
  1494. Write('; ');
  1495. if Assigned(El.Incr) then
  1496. WriteJS(El.Incr);
  1497. Writer.CurElement:=El;
  1498. Write(') ');
  1499. if Assigned(El.Body) then
  1500. WriteJS(El.Body);
  1501. end;
  1502. procedure TJSWriter.WriteWhileStatement(El: TJSWhileStatement);
  1503. begin
  1504. if El is TJSDoWhileStatement then
  1505. begin
  1506. Write('do ');
  1507. if Assigned(El.Body) then
  1508. begin
  1509. FSkipCurlyBrackets:=false;
  1510. WriteJS(El.Body);
  1511. Writer.CurElement:=El;
  1512. end;
  1513. Write(' while (');
  1514. If Assigned(El.Cond) then
  1515. begin
  1516. FSkipRoundBrackets:=true;
  1517. WriteJS(EL.Cond);
  1518. Writer.CurElement:=El;
  1519. FSkipRoundBrackets:=false;
  1520. end;
  1521. Write(')');
  1522. end
  1523. else
  1524. begin
  1525. Write('while (');
  1526. If Assigned(El.Cond) then
  1527. begin
  1528. FSkipRoundBrackets:=true;
  1529. WriteJS(EL.Cond);
  1530. Writer.CurElement:=El;
  1531. FSkipRoundBrackets:=false;
  1532. end;
  1533. Write(') ');
  1534. if Assigned(El.Body) then
  1535. WriteJS(El.Body);
  1536. end;
  1537. end;
  1538. procedure TJSWriter.WriteSwitchStatement(El: TJSSwitchStatement);
  1539. Var
  1540. C : Boolean;
  1541. I : Integer;
  1542. EC : TJSCaseElement;
  1543. begin
  1544. C:=(woCompact in Options);
  1545. Write('switch (');
  1546. If Assigned(El.Cond) then
  1547. begin
  1548. FSkipRoundBrackets:=true;
  1549. WriteJS(El.Cond);
  1550. Writer.CurElement:=El;
  1551. FSkipRoundBrackets:=false;
  1552. end;
  1553. if C then
  1554. Write(') {')
  1555. else
  1556. Writeln(') {');
  1557. For I:=0 to El.Cases.Count-1 do
  1558. begin
  1559. EC:=El.Cases[i];
  1560. if EC=El.TheDefault then
  1561. Write('default')
  1562. else
  1563. begin
  1564. Writer.CurElement:=EC.Expr;
  1565. Write('case ');
  1566. FSkipRoundBrackets:=true;
  1567. WriteJS(EC.Expr);
  1568. FSkipRoundBrackets:=false;
  1569. end;
  1570. if Assigned(EC.Body) then
  1571. begin
  1572. FSkipCurlyBrackets:=true;
  1573. If C then
  1574. Write(': ')
  1575. else
  1576. Writeln(':');
  1577. Indent;
  1578. WriteJS(EC.Body);
  1579. Undent;
  1580. if (EC.Body is TJSStatementList) or (EC.Body is TJSEmptyBlockStatement) then
  1581. begin
  1582. if C then
  1583. begin
  1584. if I<El.Cases.Count-1 then
  1585. Write(' ');
  1586. end
  1587. else
  1588. Writeln('');
  1589. end
  1590. else if C then
  1591. Write('; ')
  1592. else
  1593. Writeln(';');
  1594. end
  1595. else
  1596. begin
  1597. if C then
  1598. Write(': ')
  1599. else
  1600. Writeln(':');
  1601. end;
  1602. end;
  1603. Writer.CurElement:=El;
  1604. Write('}');
  1605. end;
  1606. procedure TJSWriter.WriteTargetStatement(El: TJSTargetStatement);
  1607. Var
  1608. TN : TJSString;
  1609. begin
  1610. TN:=El.TargetName;
  1611. if (El is TJSForStatement) then
  1612. WriteForStatement(TJSForStatement(El))
  1613. else if (El is TJSSwitchStatement) then
  1614. WriteSwitchStatement(TJSSwitchStatement(El))
  1615. else if (El is TJSForInStatement) then
  1616. WriteForInStatement(TJSForInStatement(El))
  1617. else if El is TJSWhileStatement then
  1618. WriteWhileStatement(TJSWhileStatement(El))
  1619. else if (El is TJSContinueStatement) then
  1620. begin
  1621. if (TN<>'') then
  1622. Write('continue '+TN)
  1623. else
  1624. Write('continue');
  1625. end
  1626. else if (El is TJSBreakStatement) then
  1627. begin
  1628. if (TN<>'') then
  1629. Write('break '+TN)
  1630. else
  1631. Write('break');
  1632. end
  1633. else
  1634. Error('Unknown target statement class: "%s"',[El.ClassName])
  1635. end;
  1636. procedure TJSWriter.WriteReturnStatement(El: TJSReturnStatement);
  1637. begin
  1638. if El.Expr=nil then
  1639. Write('return')
  1640. else
  1641. begin
  1642. Write('return ');
  1643. FSkipRoundBrackets:=true;
  1644. WriteJS(El.Expr);
  1645. FSkipRoundBrackets:=false;
  1646. end;
  1647. end;
  1648. procedure TJSWriter.WriteLabeledStatement(El: TJSLabeledStatement);
  1649. begin
  1650. if Assigned(El.TheLabel) then
  1651. begin
  1652. Write(El.TheLabel.Name);
  1653. if woCompact in Options then
  1654. Write(': ')
  1655. else
  1656. Writeln(':');
  1657. end;
  1658. // Target ??
  1659. WriteJS(El.A);
  1660. end;
  1661. procedure TJSWriter.WriteTryStatement(El: TJSTryStatement);
  1662. Var
  1663. C : Boolean;
  1664. begin
  1665. C:=woCompact in Options;
  1666. Write('try {');
  1667. if not IsEmptyStatement(El.Block) then
  1668. begin
  1669. if Not C then writeln('');
  1670. FSkipCurlyBrackets:=True;
  1671. Indent;
  1672. WriteJS(El.Block);
  1673. if (Not C) and (not (El.Block is TJSStatementList)) then writeln('');
  1674. Undent;
  1675. end;
  1676. Writer.CurElement:=El;
  1677. Write('}');
  1678. If (El is TJSTryCatchFinallyStatement) or (El is TJSTryCatchStatement) then
  1679. begin
  1680. Write(' catch');
  1681. if El.Ident<>'' then Write(' ('+El.Ident+')');
  1682. If C then
  1683. Write(' {')
  1684. else
  1685. Writeln(' {');
  1686. if not IsEmptyStatement(El.BCatch) then
  1687. begin
  1688. FSkipCurlyBrackets:=True;
  1689. Indent;
  1690. WriteJS(El.BCatch);
  1691. Undent;
  1692. if (Not C) and (not (El.BCatch is TJSStatementList)) then writeln('');
  1693. end;
  1694. Writer.CurElement:=El;
  1695. Write('}');
  1696. end;
  1697. If (El is TJSTryCatchFinallyStatement) or (El is TJSTryFinallyStatement) then
  1698. begin
  1699. If C then
  1700. Write(' finally {')
  1701. else
  1702. Writeln(' finally {');
  1703. if not IsEmptyStatement(El.BFinally) then
  1704. begin
  1705. Indent;
  1706. FSkipCurlyBrackets:=True;
  1707. WriteJS(El.BFinally);
  1708. Undent;
  1709. if (Not C) and (not (El.BFinally is TJSStatementList)) then writeln('');
  1710. end;
  1711. Writer.CurElement:=El;
  1712. Write('}');
  1713. end;
  1714. end;
  1715. procedure TJSWriter.WriteFunctionBody(El: TJSFunctionBody);
  1716. begin
  1717. //writeln('TJSWriter.WriteFunctionBody '+El.A.ClassName+' FSkipBrackets='+BoolToStr(FSkipCurlyBrackets,'true','false'));
  1718. if not IsEmptyStatement(El.A) then
  1719. WriteJS(El.A);
  1720. end;
  1721. procedure TJSWriter.WriteFunctionDeclarationStatement(
  1722. El: TJSFunctionDeclarationStatement);
  1723. begin
  1724. if Assigned(El.AFunction) then
  1725. WriteFuncDef(El.AFunction);
  1726. end;
  1727. procedure TJSWriter.WriteSourceElements(El: TJSSourceElements);
  1728. Var
  1729. C : Boolean;
  1730. Procedure WriteElements(Elements: TJSElementNodes);
  1731. Var
  1732. I : Integer;
  1733. E : TJSElement;
  1734. begin
  1735. if Elements=nil then exit;
  1736. For I:=0 to Elements.Count-1 do
  1737. begin
  1738. E:=Elements.Nodes[i].Node;
  1739. WriteJS(E);
  1740. if Not C then
  1741. WriteLn(';')
  1742. else
  1743. if I<Elements.Count-1 then
  1744. Write('; ')
  1745. else
  1746. Write(';')
  1747. end;
  1748. end;
  1749. begin
  1750. C:=(woCompact in Options);
  1751. WriteElements(El.Vars);
  1752. WriteElements(El.Functions);
  1753. WriteElements(El.Statements);
  1754. end;
  1755. procedure TJSWriter.WriteVariableStatement(El: TJSVariableStatement);
  1756. begin
  1757. Write('var ');
  1758. FSkipRoundBrackets:=true;
  1759. WriteJS(El.A);
  1760. end;
  1761. procedure TJSWriter.WriteJS(El: TJSElement);
  1762. var
  1763. C: TClass;
  1764. begin
  1765. {$IFDEF DEBUGJSWRITER}
  1766. if (El<>Nil) then
  1767. system.Writeln('WriteJS : ',El.ClassName,' ',El.Line,',',El.Column)
  1768. else
  1769. system.Writeln('WriteJS : El = Nil');
  1770. {$ENDIF}
  1771. Writer.CurElement:=El;
  1772. C:=El.ClassType;
  1773. if (C=TJSEmptyBlockStatement ) then
  1774. WriteEmptyBlockStatement(TJSEmptyBlockStatement(El))
  1775. else if (C=TJSEmptyStatement) then
  1776. WriteEmptyStatement(TJSEmptyStatement(El))
  1777. else if (C=TJSLiteral) then
  1778. WriteLiteral(TJSLiteral(El))
  1779. else if C.InheritsFrom(TJSPrimaryExpression) then
  1780. WritePrimaryExpression(TJSPrimaryExpression(El))
  1781. else if C.InheritsFrom(TJSArrayLiteral) then
  1782. WriteArrayLiteral(TJSArrayLiteral(El))
  1783. else if (C=TJSObjectLiteral) then
  1784. WriteObjectLiteral(TJSObjectLiteral(El))
  1785. else if C.InheritsFrom(TJSMemberExpression) then
  1786. WriteMemberExpression(TJSMemberExpression(El))
  1787. else if (C=TJSRegularExpressionLiteral) then
  1788. WriteRegularExpressionLiteral(TJSRegularExpressionLiteral(El))
  1789. else if (C=TJSCallExpression) then
  1790. WriteCallExpression(TJSCallExpression(El))
  1791. else if (C=TJSLabeledStatement) then // Before unary
  1792. WriteLabeledStatement(TJSLabeledStatement(El))
  1793. else if (C=TJSFunctionBody) then // Before unary
  1794. WriteFunctionBody(TJSFunctionBody(El))
  1795. else if (C=TJSVariableStatement) then // Before unary
  1796. WriteVariableStatement(TJSVariableStatement(El))
  1797. else if C.InheritsFrom(TJSUnary) then
  1798. WriteUnary(TJSUnary(El))
  1799. else if (C=TJSVariableDeclarationList) then
  1800. WriteVarDeclarationList(TJSVariableDeclarationList(El)) // Must be before binary
  1801. else if (C=TJSStatementList) then
  1802. WriteStatementList(TJSStatementList(El)) // Must be before binary
  1803. else if (C=TJSWithStatement) then
  1804. WriteWithStatement(TJSWithStatement(El)) // Must be before binary
  1805. else if C.InheritsFrom(TJSBinary) then
  1806. WriteBinary(TJSBinary(El))
  1807. else if (C=TJSConditionalExpression) then
  1808. WriteConditionalExpression(TJSConditionalExpression(El))
  1809. else if C.InheritsFrom(TJSAssignStatement) then
  1810. WriteAssignStatement(TJSAssignStatement(El))
  1811. else if (C=TJSVarDeclaration) then
  1812. WriteVarDeclaration(TJSVarDeclaration(El))
  1813. else if (C=TJSIfStatement) then
  1814. WriteIfStatement(TJSIfStatement(El))
  1815. else if C.InheritsFrom(TJSTargetStatement) then
  1816. WriteTargetStatement(TJSTargetStatement(El))
  1817. else if (C=TJSReturnStatement) then
  1818. WriteReturnStatement(TJSReturnStatement(El))
  1819. else if C.InheritsFrom(TJSTryStatement) then
  1820. WriteTryStatement(TJSTryStatement(El))
  1821. else if (C=TJSFunctionDeclarationStatement) then
  1822. WriteFunctionDeclarationStatement(TJSFunctionDeclarationStatement(El))
  1823. else if (C=TJSSourceElements) then
  1824. WriteSourceElements(TJSSourceElements(El))
  1825. else if El=Nil then
  1826. Error(SErrNilNode)
  1827. else
  1828. Error(SErrUnknownJSClass,[El.ClassName]);
  1829. // Write('/* '+El.ClassName+' */');
  1830. FSkipCurlyBrackets:=False;
  1831. end;
  1832. {AllowWriteln-}
  1833. {$ifdef HasFileWriter}
  1834. { TFileWriter }
  1835. Function TFileWriter.DoWrite(Const S: TJSWriterString) : Integer;
  1836. begin
  1837. Result:=Length(S);
  1838. {$ifdef NodeJS}
  1839. system.writeln('TFileWriter.DoWrite ToDo ',S);
  1840. {$else}
  1841. system.Write(FFile,S);
  1842. {$endif}
  1843. end;
  1844. {$ifdef FPC_HAS_CPSTRING}
  1845. Function TFileWriter.DoWrite(Const S: UnicodeString) : Integer;
  1846. begin
  1847. Result:=Length(S)*SizeOf(UnicodeChar);
  1848. system.Write(FFile,S);
  1849. end;
  1850. {$endif}
  1851. Constructor TFileWriter.Create(Const AFileName: String);
  1852. begin
  1853. inherited Create;
  1854. FFileName:=AFileName;
  1855. {$ifdef NodeJS}
  1856. system.writeln('TFileWriter.Create ToDo ',AFileName);
  1857. {$else}
  1858. Assign(FFile,AFileName);
  1859. Rewrite(FFile);
  1860. {$endif}
  1861. end;
  1862. Destructor TFileWriter.Destroy;
  1863. begin
  1864. Close;
  1865. Inherited;
  1866. end;
  1867. Procedure TFileWriter.Flush;
  1868. begin
  1869. {$ifdef NodeJS}
  1870. system.writeln('TFileWriter.Flush ToDO');
  1871. {$else}
  1872. system.Flush(FFile);
  1873. {$endif}
  1874. end;
  1875. Procedure TFileWriter.Close;
  1876. begin
  1877. {$ifdef NodeJS}
  1878. system.writeln('TFileWriter.DoWrite ToDo ');
  1879. {$else}
  1880. system.Close(FFile);
  1881. {$endif}
  1882. end;
  1883. {$endif}
  1884. { TTextWriter }
  1885. procedure TTextWriter.SetCurElement(const AValue: TJSElement);
  1886. begin
  1887. FCurElement:=AValue;
  1888. end;
  1889. procedure TTextWriter.Writing;
  1890. begin
  1891. if Assigned(OnWriting) then
  1892. OnWriting(Self);
  1893. end;
  1894. constructor TTextWriter.Create;
  1895. begin
  1896. FCurLine:=1;
  1897. FCurColumn:=1;
  1898. FLineBreak:=sLineBreak;
  1899. end;
  1900. {$ifdef FPC_HAS_CPSTRING}
  1901. function TTextWriter.Write(const S: UnicodeString): Integer;
  1902. var
  1903. p: PWideChar;
  1904. c: WideChar;
  1905. begin
  1906. if S='' then exit;
  1907. Writing;
  1908. Result:=DoWrite(S);
  1909. p:=PWideChar(S);
  1910. repeat
  1911. c:=p^;
  1912. case c of
  1913. #0:
  1914. if p-PWideChar(S)=length(S)*2 then
  1915. break
  1916. else
  1917. inc(FCurColumn);
  1918. #10,#13:
  1919. begin
  1920. FCurColumn:=1;
  1921. inc(FCurLine);
  1922. inc(p);
  1923. if (p^ in [#10,#13]) and (c<>p^) then inc(p);
  1924. continue;
  1925. end;
  1926. else
  1927. // ignore low/high surrogate, CurColumn is char index, not codepoint
  1928. inc(FCurColumn);
  1929. end;
  1930. inc(p);
  1931. until false;
  1932. end;
  1933. {$endif}
  1934. function TTextWriter.Write(const S: TJSWriterString): Integer;
  1935. var
  1936. c: Char;
  1937. l, p: Integer;
  1938. begin
  1939. if S='' then exit;
  1940. Writing;
  1941. Result:=DoWrite(S);
  1942. l:=length(S);
  1943. p:=1;
  1944. while p<=l do
  1945. begin
  1946. c:=S[p];
  1947. case c of
  1948. #10,#13:
  1949. begin
  1950. FCurColumn:=1;
  1951. inc(FCurLine);
  1952. inc(p);
  1953. if (p<=l) and (S[p] in [#10,#13]) and (c<>S[p]) then inc(p);
  1954. end;
  1955. else
  1956. // Note about UTF-8 multibyte chars: CurColumn is char index, not codepoint
  1957. inc(FCurColumn);
  1958. inc(p);
  1959. end;
  1960. end;
  1961. end;
  1962. function TTextWriter.WriteLn(const S: TJSWriterString): Integer;
  1963. begin
  1964. Result:=Write(S)+Write(LineBreak);
  1965. end;
  1966. function TTextWriter.Write(const Fmt: TJSWriterString;
  1967. Args: array of const): Integer;
  1968. begin
  1969. Result:=Write(Format(Fmt,Args));
  1970. end;
  1971. function TTextWriter.WriteLn(const Fmt: TJSWriterString;
  1972. Args: array of const): Integer;
  1973. begin
  1974. Result:=WriteLn(Format(Fmt,Args));
  1975. end;
  1976. function TTextWriter.Write(const Args: array of const): Integer;
  1977. Var
  1978. I : Integer;
  1979. {$ifdef pas2js}
  1980. V: jsvalue;
  1981. S: TJSWriterString;
  1982. {$else}
  1983. V : TVarRec;
  1984. S : String;
  1985. U : UnicodeString;
  1986. {$endif}
  1987. begin
  1988. Result:=0;
  1989. For I:=Low(Args) to High(Args) do
  1990. begin
  1991. V:=Args[i];
  1992. S:='';
  1993. {$ifdef pas2js}
  1994. case jsTypeOf(V) of
  1995. 'boolean':
  1996. if V then S:='true' else S:='false';
  1997. 'number':
  1998. if isInteger(V) then
  1999. S:=str(NativeInt(V))
  2000. else
  2001. S:=str(Double(V));
  2002. 'string':
  2003. S:=String(V);
  2004. else continue;
  2005. end;
  2006. Result:=Result+Write(S);
  2007. {$else}
  2008. U:='';
  2009. case V.VType of
  2010. vtInteger : Str(V.VInteger,S);
  2011. vtBoolean : if V.VBoolean then s:='true' else s:='false';
  2012. vtChar : s:=V.VChar;
  2013. vtWideChar : U:=V.VWideChar;
  2014. vtExtended : Str(V.VExtended^,S);
  2015. vtString : S:=V.VString^;
  2016. vtPChar : S:=V.VPChar;
  2017. vtPWideChar : U:=V.VPWideChar;
  2018. vtAnsiString : S:=PChar(V.VAnsiString);
  2019. vtCurrency : Str(V.VCurrency^,S);
  2020. vtVariant : S:=V.VVariant^;
  2021. vtWideString : U:=PWideChar(V.VWideString);
  2022. vtInt64 : Str(V.VInt64^,S);
  2023. vtUnicodeString : U:=PWideChar(V.VUnicodeString);
  2024. vtQWord : Str(V.VQWord^,S);
  2025. end;
  2026. if (U<>'') then
  2027. Result:=Result+Write(u)
  2028. else if (S<>'') then
  2029. Result:=Result+Write(s);
  2030. {$endif}
  2031. end;
  2032. end;
  2033. function TTextWriter.WriteLn(const Args: array of const): Integer;
  2034. begin
  2035. Result:=Write(Args)+Writeln('');
  2036. end;
  2037. end.