1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218 |
- { *********************************************************************
- This file is part of the Free Component Library (FCL)
- Copyright (c) 2016 Michael Van Canneyt.
-
- Javascript minifier
-
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
- **********************************************************************}
- unit jswriter;
- {$i fcl-js.inc}
- { $DEFINE DEBUGJSWRITER}
- interface
- uses
- {$ifdef pas2js}
- JS,
- {$endif}
- SysUtils, jsbase, jstree;
- Type
- {$ifdef pas2js}
- TJSWriterString = UnicodeString;
- TJSWriterChar = WideChar;
- {$else}
- TJSWriterString = AnsiString;
- TJSWriterChar = AnsiChar;
- {$endif}
- TTextWriter = class;
- TTextWriterWriting = procedure(Sender: TTextWriter) of object;
- { TTextWriter }
- TTextWriter = Class(TObject)
- private
- FCurElement: TJSElement;
- FCurLine: integer;
- FCurColumn: integer;
- FLineBreak: string;
- FOnWriting: TTextWriterWriting;
- protected
- Function DoWrite(Const S : TJSWriterString) : Integer; virtual; abstract;
- {$ifdef FPC_HAS_CPSTRING}
- Function DoWrite(Const S : UnicodeString) : Integer; virtual; abstract;
- {$endif}
- procedure SetCurElement(const AValue: TJSElement); virtual;
- Procedure Writing; virtual; // called before adding new characters
- Public
- // All functions return the number of bytes copied to output stream.
- constructor Create;
- {$ifdef FPC_HAS_CPSTRING}
- Function Write(Const S : UnicodeString) : Integer;
- {$endif}
- Function Write(Const S : TJSWriterString) : Integer;
- Function WriteLn(Const S : TJSWriterString) : Integer;
- Function Write(Const Fmt : TJSWriterString; Args : Array of const) : Integer;
- Function WriteLn(Const Fmt : TJSWriterString; Args : Array of const) : Integer;
- Function Write(Const Args : Array of const) : Integer;
- Function WriteLn(Const Args : Array of const) : Integer;
- Property CurLine: integer read FCurLine write FCurLine;
- Property CurColumn: integer read FCurColumn write FCurColumn;// char index, not codepoint
- Property CurElement: TJSElement read FCurElement write SetCurElement;
- Property OnWriting: TTextWriterWriting read FOnWriting write FOnWriting;
- Property LineBreak: string read FLineBreak write FLineBreak;
- end;
- {$ifdef HasFileWriter}
- { TFileWriter }
- TFileWriter = Class(TTextWriter)
- Protected
- {$ifdef NodeJS}
- {$else}
- FFile : Text;
- {$endif}
- FFileName : String;
- Function DoWrite(Const S : TJSWriterString) : Integer; override;
- {$ifdef FPC_HAS_CPSTRING}
- Function DoWrite(Const S : UnicodeString) : Integer; override;
- {$endif}
- Public
- Constructor Create(Const AFileName : String); reintroduce;
- Destructor Destroy; override;
- Procedure Flush;
- Procedure Close;
- Property FileName : String Read FFileName;
- end;
- {$endif}
- TBufferWriter_Buffer = Array of {$ifdef fpc}byte{$else}string{$endif};
- { TBufferWriter }
- TBufferWriter = Class(TTextWriter)
- private type
- TBuffer = TBufferWriter_Buffer;
- private
- FBufPos,
- FCapacity: Cardinal;
- FBuffer: TBuffer;
- function GetAsString: TJSWriterString;
- {$ifdef fpc}
- function GetBuffer: Pointer;
- {$endif}
- function GetBufferLength: Integer;
- function GetCapacity: Cardinal;
- {$ifdef FPC_HAS_CPSTRING}
- function GetUnicodeString: UnicodeString;
- {$endif}
- procedure SetAsString(const AValue: TJSWriterString);
- procedure SetCapacity(AValue: Cardinal);
- Protected
- Function DoWrite(Const S : TJSWriterString) : integer; override;
- {$ifdef FPC_HAS_CPSTRING}
- Function DoWrite(Const S : UnicodeString) : integer; override;
- {$endif}
- Public
- Constructor Create(Const ACapacity : Cardinal); reintroduce;
- {$ifdef fpc}
- Procedure SaveToFile(Const AFileName : String);
- Property Buffer : Pointer Read GetBuffer;
- {$endif}
- {$ifdef pas2js}
- Property Buffer: TBufferWriter_Buffer read FBuffer;
- {$endif}
- Property BufferLength : Integer Read GetBufferLength;
- Property Capacity : Cardinal Read GetCapacity Write SetCapacity;
- Property AsString : TJSWriterString Read GetAsString Write SetAsString;
- {$ifdef FPC_HAS_CPSTRING}
- Property AsAnsiString : AnsiString Read GetAsString; deprecated 'use AsString instead, fpc 3.3.1';
- Property AsUnicodeString : UnicodeString Read GetUnicodeString;
- {$endif}
- end;
- TJSEscapeQuote = (
- jseqSingle,
- jseqDouble,
- jseqBoth
- );
- { TJSWriter }
- TWriteOption = (woCompact,
- {$ifdef FPC_HAS_CPSTRING}
- woUseUTF8,
- {$endif}
- woTabIndent,
- woEmptyStatementAsComment,
- woQuoteElementNames,
- woCompactArrayLiterals,
- woCompactObjectLiterals,
- woCompactArguments);
- TWriteOptions = Set of TWriteOption;
- TJSWriter = Class
- private
- FCurIndent : Integer;
- FFreeWriter : Boolean;
- FIndentChar : Char;
- FIndentSize: Byte;
- FLastChar: WideChar;
- FLinePos : Integer;
- FOptions: TWriteOptions;
- FSkipCurlyBrackets : Boolean;
- FSkipRoundBrackets : Boolean;
- FWriter: TTextWriter;
- function GetUseUTF8: Boolean;
- procedure SetOptions(AValue: TWriteOptions);
- Protected
- // Helper routines
- Procedure Error(Const Msg : TJSWriterString);
- Procedure Error(Const Fmt : TJSWriterString; Args : Array of const);
- Procedure WriteIndent; // inline;
- {$ifdef FPC_HAS_CPSTRING}
- Procedure Write(Const U : UnicodeString);
- {$endif}
- Procedure Write(Const S : TJSWriterString);
- Procedure WriteLn(Const S : TJSWriterString);
- {$ifdef FPC_HAS_CPSTRING}
- Procedure WriteLn(Const U : UnicodeString);
- {$endif}
- // one per type of statement
- Procedure WriteValue(V : TJSValue); virtual;
- Procedure WriteRegularExpressionLiteral(El: TJSRegularExpressionLiteral);
- Procedure WriteVariableStatement(El: TJSVariableStatement);
- Procedure WriteEmptyBlockStatement(El: TJSEmptyBlockStatement); virtual;
- Procedure WriteEmptyStatement(El: TJSEmptyStatement);virtual;
- Procedure WriteLiteral(El: TJSLiteral);virtual;
- Procedure WriteArrayLiteral(El: TJSArrayLiteral);virtual;
- Procedure WriteObjectLiteral(El: TJSObjectLiteral);virtual;
- Procedure WriteMemberExpression(El: TJSMemberExpression);virtual;
- Procedure WriteCallExpression(El: TJSCallExpression);virtual;
- Procedure WriteSwitchStatement(El: TJSSwitchStatement);virtual;
- Procedure WriteUnary(El: TJSUnary);virtual;
- Procedure WriteAssignStatement(El: TJSAssignStatement);virtual;
- Procedure WriteForInStatement(El: TJSForInStatement);virtual;
- Procedure WriteWhileStatement(El: TJSWhileStatement);virtual;
- Procedure WriteForStatement(El: TJSForStatement);virtual;
- Procedure WriteIfStatement(El: TJSIfStatement);virtual;
- Procedure WriteSourceElements(El: TJSSourceElements);virtual;
- Procedure WriteStatementList(El: TJSStatementList);virtual;
- Procedure WriteTryStatement(El: TJSTryStatement);virtual;
- Procedure WriteVarDeclaration(El: TJSVarDeclaration);virtual;
- Procedure WriteWithStatement(El: TJSWithStatement);virtual;
- Procedure WriteVarDeclarationList(El: TJSVariableDeclarationList);virtual;
- Procedure WriteConditionalExpression(El: TJSConditionalExpression);virtual;
- Procedure WriteFunctionBody(El: TJSFunctionBody);virtual;
- Procedure WriteFunctionDeclarationStatement(El: TJSFunctionDeclarationStatement);virtual;
- Procedure WriteLabeledStatement(El: TJSLabeledStatement);virtual;
- Procedure WriteReturnStatement(El: TJSReturnStatement);virtual;
- Procedure WriteTargetStatement(El: TJSTargetStatement);virtual;
- Procedure WriteFuncDef(FD: TJSFuncDef);virtual;
- Procedure WritePrimaryExpression(El: TJSPrimaryExpression);virtual;
- Procedure WriteBinary(El: TJSBinary);virtual;
- Function IsEmptyStatement(El: TJSElement): boolean;
- Function HasLineEnding(El: TJSElement): boolean;
- Public
- Function EscapeString(const S: TJSString; Quote: TJSEscapeQuote = jseqDouble): TJSString;
- Constructor Create(AWriter : TTextWriter);
- {$ifdef HasFileWriter}
- Constructor Create(Const AFileName : String);
- {$endif}
- Destructor Destroy; override;
- Procedure WriteJS(El : TJSElement);
- Procedure Indent;
- Procedure Undent;
- Property Writer : TTextWriter Read FWriter;
- Property Options : TWriteOptions Read FOptions Write SetOptions;
- Property IndentSize : Byte Read FIndentSize Write FIndentSize;
- Property UseUTF8 : Boolean Read GetUseUTF8;
- Property LastChar: WideChar read FLastChar;
- Property SkipCurlyBrackets : Boolean read FSkipCurlyBrackets write FSkipCurlyBrackets;
- Property SkipRoundBrackets : Boolean read FSkipRoundBrackets write FSkipRoundBrackets;
- end;
- EJSWriter = Class(Exception);
- {$ifdef FPC_HAS_CPSTRING}
- Function UTF16ToUTF8(const S: UnicodeString): string;
- {$endif}
- Function QuoteJSString(const S: TJSString; Quote: TJSChar = #0): TJSString;
- implementation
- Resourcestring
- SErrUnknownJSClass = 'Unknown javascript element class : %s';
- SErrNilNode = 'Nil node in Javascript';
- {$ifdef FPC_HAS_CPSTRING}
- function HexDump(p: PChar; Count: integer): string;
- var
- i: Integer;
- begin
- Result:='';
- for i:=0 to Count-1 do
- Result:=Result+HexStr(ord(p[i]),2);
- end;
- function UTF16ToUTF8(const S: UnicodeString): string;
- begin
- Result:=UTF8Encode(S);
- // prevent UTF8 codepage appear in the strings - we don't need codepage
- // conversion magic
- SetCodePage(RawByteString(Result), CP_ACP, False);
- end;
- {$endif}
- function QuoteJSString(const S: TJSString; Quote: TJSChar): TJSString;
- var
- i, j, Count: Integer;
- begin
- if Quote=#0 then
- begin
- if Pos('"',S)>0 then
- Quote:=''''
- else
- Quote:='"';
- end;
- Result := '' + Quote;
- Count := length(S);
- i := 0;
- j := 0;
- while i < Count do
- begin
- inc(i);
- if S[i] = Quote then
- begin
- Result := Result + copy(S, 1 + j, i - j) + Quote;
- j := i;
- end;
- end;
- if i <> j then
- Result := Result + copy(S, 1 + j, i - j);
- Result := Result + Quote;
- end;
- { TBufferWriter }
- function TBufferWriter.GetBufferLength: Integer;
- begin
- Result:=FBufPos;
- end;
- function TBufferWriter.GetAsString: TJSWriterString;
- begin
- {$ifdef pas2js}
- if FBufPos<length(FBuffer) then
- TJSArray(FBuffer).Length:=FBufPos;
- Result:=TJSArray(FBuffer).join('');
- {$else}
- Result:='';
- SetLength(Result,BufferLength);
- if (BufferLength>0) then
- Move(FBuffer[0],Result[1],BufferLength);
- {$endif}
- end;
- {$ifdef fpc}
- function TBufferWriter.GetBuffer: Pointer;
- begin
- Result:=Pointer(FBuffer);
- end;
- {$endif}
- function TBufferWriter.GetCapacity: Cardinal;
- begin
- Result:=Length(FBuffer);
- end;
- {$ifdef FPC_HAS_CPSTRING}
- function TBufferWriter.GetUnicodeString: UnicodeString;
- Var
- SL : Integer;
- begin
- SL:=BufferLength div SizeOf(UnicodeChar); // Silently ignores last byte
- Result:='';
- SetLength(Result,SL);
- if (SL>0) then
- Move(FBuffer[0],Result[1],SL*SizeOf(UnicodeChar));
- end;
- {$endif}
- procedure TBufferWriter.SetAsString(const AValue: TJSWriterString);
- begin
- {$ifdef pas2js}
- SetLength(FBuffer,0);
- FCapacity:=0;
- {$endif}
- FBufPos:=0;
- DoWrite(AValue);
- end;
- procedure TBufferWriter.SetCapacity(AValue: Cardinal);
- begin
- if FCapacity=AValue then Exit;
- SetLength(FBuffer,AValue);
- if (FBufPos>Capacity) then
- FBufPos:=Capacity;
- end;
- function TBufferWriter.DoWrite(const S: TJSWriterString): integer;
- {$ifdef pas2js}
- begin
- Result:=Length(S)*2;
- if Result=0 then exit;
- TJSArray(FBuffer).push(S);
- inc(FBufPos);
- FCapacity:=FBufPos;
- end;
- {$else}
- Var
- DesLen,MinLen : Integer;
- begin
- Result:=Length(S)*SizeOf(TJSWriterChar);
- if Result=0 then exit;
- MinLen:=Result+integer(FBufPos);
- If (MinLen>integer(Capacity)) then
- begin
- DesLen:=(FCapacity*3) div 2;
- if DesLen>MinLen then
- MinLen:=DesLen;
- Capacity:=MinLen;
- end;
- Move(S[1],FBuffer[FBufPos],Result);
- FBufPos:=integer(FBufPos)+Result;
- end;
- {$endif}
- {$ifdef FPC_HAS_CPSTRING}
- function TBufferWriter.DoWrite(const S: UnicodeString): integer;
- Var
- DesLen,MinLen : Integer;
- begin
- Result:=Length(S)*SizeOf(UnicodeChar);
- if Result=0 then exit;
- MinLen:=Result+integer(FBufPos);
- If (MinLen>integer(Capacity)) then
- begin
- DesLen:=(FCapacity*3) div 2;
- if DesLen>MinLen then
- MinLen:=DesLen;
- Capacity:=MinLen;
- end;
- Move(S[1],FBuffer[FBufPos],Result);
- FBufPos:=integer(FBufPos)+Result;
- end;
- {$endif}
- constructor TBufferWriter.Create(const ACapacity: Cardinal);
- begin
- inherited Create;
- Capacity:=ACapacity;
- end;
- {$ifdef fpc}
- procedure TBufferWriter.SaveToFile(const AFileName: String);
- Var
- F : File;
- begin
- Assign(F,AFileName);
- Rewrite(F,1);
- try
- BlockWrite(F,FBuffer[0],FBufPos);
- finally
- Close(F);
- end;
- end;
- {$endif}
- { TJSWriter }
- {AllowWriteln}
- procedure TJSWriter.SetOptions(AValue: TWriteOptions);
- begin
- if FOptions=AValue then Exit;
- FOptions:=AValue;
- If woTabIndent in FOptions then
- FIndentChar:=#9
- else
- FIndentChar:=' ';
- end;
- function TJSWriter.GetUseUTF8: Boolean;
- begin
- Result:={$ifdef FPC_HAS_CPSTRING}(woUseUTF8 in Options){$else}false{$endif};
- end;
- procedure TJSWriter.Error(const Msg: TJSWriterString);
- begin
- Raise EJSWriter.Create(Msg);
- end;
- procedure TJSWriter.Error(const Fmt: TJSWriterString;
- Args: array of const);
- begin
- Raise EJSWriter.CreateFmt(Fmt,Args);
- end;
- procedure TJSWriter.WriteIndent;
- begin
- If (FLinePos=0) and (FCurIndent>0) then
- begin
- FLinePos:=Writer.Write(StringOfChar(FIndentChar,FCurIndent));
- FLastChar:=WideChar(FIndentChar);
- end;
- end;
- procedure TJSWriter.Indent;
- begin
- Inc(FCurIndent,FIndentSIze);
- end;
- procedure TJSWriter.Undent;
- begin
- if (FCurIndent>=FIndentSIze) then
- Dec(FCurIndent,FIndentSIze)
- else
- FCurIndent:=0;
- end;
- {$ifdef FPC_HAS_CPSTRING}
- procedure TJSWriter.Write(const U: UnicodeString);
- Var
- S : String;
- begin
- //system.writeln('TJSWriter.Write unicodestring=',U);
- WriteIndent;
- if UseUTF8 then
- begin
- S:=UTF16ToUTF8(U);
- if S='' then exit;
- FLinePos:=FLinePos+Writer.Write(S);
- FLastChar:=WideChar(S[length(S)]);
- end
- else if U<>'' then
- begin
- FLinePos:=FLinePos+Writer.Write(U);
- FLastChar:=U[length(U)];
- end;
- end;
- {$endif}
- procedure TJSWriter.Write(const S: TJSWriterString);
- begin
- //system.writeln('TJSWriter.Write TJSWriterString=',S);
- {$ifdef FPC_HAS_CPSTRING}
- if Not (woUseUTF8 in Options) then
- Write(UnicodeString(S))
- else
- {$endif}
- begin
- WriteIndent;
- if s='' then exit;
- FLinePos:=FLinePos+Writer.Write(S);
- FLastChar:=WideChar(S[length(S)]);
- end;
- end;
- procedure TJSWriter.WriteLn(const S: TJSWriterString);
- begin
- {$ifdef FPC_HAS_CPSTRING}
- if Not (woUseUTF8 in Options) then
- Writeln(UnicodeString(S))
- else
- {$endif}
- begin
- WriteIndent;
- Writer.WriteLn(S);
- FLastChar:=WideChar(#10);
- FLinePos:=0;
- end;
- end;
- {$ifdef FPC_HAS_CPSTRING}
- procedure TJSWriter.WriteLn(const U: UnicodeString);
- Var
- S : String;
- begin
- if UseUTF8 then
- begin
- S:=UTF16ToUTF8(U);
- Writeln(S);
- end
- else
- begin
- WriteIndent;
- FLinePos:=FLinePos+Writer.Write(U);
- Writer.WriteLn('');
- FLastChar:=WideChar(#10);
- FLinePos:=0;
- end;
- end;
- {$endif}
- function TJSWriter.EscapeString(const S: TJSString; Quote: TJSEscapeQuote
- ): TJSString;
- Var
- I,J,L : Integer;
- R: TJSString;
- c: WideChar;
- begin
- //system.writeln('TJSWriter.EscapeString "',S,'"');
- I:=1;
- J:=1;
- R:='';
- L:=Length(S);
- While I<=L do
- begin
- c:=S[I];
- if (c in [#0..#31,'"','''','/','\'])
- or (c>=#$ff00) or ((c>=#$D800) and (c<=#$DFFF)) then
- begin
- R:=R+Copy(S,J,I-J);
- Case c of
- '\' : R:=R+'\\';
- '/' : R:=R+'\/';
- '"' : if Quote=jseqSingle then R:=R+'"' else R:=R+'\"';
- '''': if Quote=jseqDouble then R:=R+'''' else R:=R+'\''';
- #0..#7,#11,#14..#31: R:=R+'\x'+TJSString(hexStr(ord(c),2));
- #8 : R:=R+'\b';
- #9 : R:=R+'\t';
- #10 : R:=R+'\n';
- #12 : R:=R+'\f';
- #13 : R:=R+'\r';
- #$D800..#$DFFF:
- begin
- if (I<L) then
- begin
- c:=S[I+1];
- if (c>=#$D000) and (c<=#$DFFF) then
- begin
- inc(I,2); // surrogate, two char codepoint
- continue;
- end;
- // invalid UTF-16, cannot be encoded as UTF-8 -> encode as hex
- R:=R+'\u'+TJSString(HexStr(ord(S[i]),4));
- end
- else
- // invalid UTF-16 at end of string, cannot be encoded as UTF-8 -> encode as hex
- R:=R+'\u'+TJSString(HexStr(ord(c),4));
- end;
- #$FF00..#$FFFF: R:=R+'\u'+TJSString(HexStr(ord(c),4));
- end;
- J:=I+1;
- end;
- Inc(I);
- end;
- R:=R+Copy(S,J,I-1);
- Result:=R;
- //system.writeln('TJSWriter.EscapeString Result="',Result,'"');
- end;
- procedure TJSWriter.WriteValue(V: TJSValue);
- const
- TabWidth = 4;
- function GetLineIndent(const S: TJSString; var p: integer): integer;
- var
- h, l: integer;
- begin
- h:=p;
- l:=length(S);
- Result:=0;
- while h<=l do
- begin
- case S[h] of
- #9: Result:=Result+(TabWidth-Result mod TabWidth);
- ' ': inc(Result);
- else break;
- end;
- inc(h);
- end;
- p:=h;
- end;
- function SkipToNextLineEnd(const S: TJSString; p: integer): integer;
- var
- l: SizeInt;
- begin
- l:=length(S);
- while (p<=l) and not (S[p] in [#10,#13]) do inc(p);
- Result:=p;
- end;
- function SkipToNextLineStart(const S: TJSString; p: integer): integer;
- var
- l: Integer;
- begin
- l:=length(S);
- while p<=l do
- begin
- case S[p] of
- #10,#13:
- begin
- if (p<l) and (S[p+1] in [#10,#13]) and (S[p]<>S[p+1]) then
- inc(p,2)
- else
- inc(p);
- break;
- end
- else inc(p);
- end;
- end;
- Result:=p;
- end;
- Var
- S , S2: String;
- JS: TJSString;
- p, StartP: Integer;
- MinIndent, CurLineIndent, j, Exp, Code: Integer;
- i: SizeInt;
- D, AsNumber: TJSNumber;
- begin
- if V.CustomValue<>'' then
- begin
- JS:=V.CustomValue;
- if JS='' then exit;
- p:=SkipToNextLineStart(JS,1);
- if p>length(JS) then
- begin
- // simple value
- Write(JS);
- exit;
- end;
- // multi line value
- // find minimum indent
- MinIndent:=-1;
- repeat
- CurLineIndent:=GetLineIndent(JS,p);
- if (MinIndent<0) or (MinIndent>CurLineIndent) then
- MinIndent:=CurLineIndent;
- p:=SkipToNextLineStart(JS,p);
- until p>length(JS);
- // write value lines indented
- p:=1;
- GetLineIndent(JS,p); // the first line is already indented, skip
- repeat
- StartP:=p;
- p:=SkipToNextLineEnd(JS,StartP);
- Write(copy(JS,StartP,p-StartP));
- if p>length(JS) then break;
- Write(sLineBreak);
- p:=SkipToNextLineStart(JS,p);
- CurLineIndent:=GetLineIndent(JS,p);
- Write(StringOfChar(FIndentChar,FCurIndent+CurLineIndent-MinIndent));
- until false;
- exit;
- end;
- Case V.ValueType of
- jstUNDEFINED : S:='undefined';
- jstNull : s:='null';
- jstBoolean : if V.AsBoolean then s:='true' else s:='false';
- jstString :
- begin
- JS:=V.AsString;
- if Pos('"',JS)>0 then
- JS:=''''+EscapeString(JS,jseqSingle)+''''
- else
- JS:='"'+EscapeString(JS,jseqDouble)+'"';
- Write(JS);
- exit;
- end;
- jstNumber :
- begin
- AsNumber:=V.AsNumber;
- if (Frac(AsNumber)=0)
- and (AsNumber>=double(MinSafeIntDouble))
- and (AsNumber<=double(MaxSafeIntDouble)) then
- begin
- Str(Round(AsNumber),S);
- end
- else
- begin
- Str(AsNumber,S);
- if S[1]=' ' then Delete(S,1,1);
- i:=Pos('E',S);
- if (i>2) then
- begin
- j:=i-2;
- case s[j] of
- '0':
- begin
- // check for 1.2340000000000001E...
- while (j>1) and (s[j]='0') do dec(j);
- if s[j]='.' then inc(j);
- S2:=LeftStr(S,j)+copy(S,i,length(S));
- val(S2,D,Code);
- if (Code=0) and (D=AsNumber) then
- S:=S2;
- end;
- '9':
- begin
- // check for 1.234999999999991E...
- while (j>1) and (s[j]='9') do dec(j);
- // cut '99999'
- S2:=LeftStr(S,j)+copy(S,i,length(S));
- if S[j]='.' then
- Insert('0',S2,j+1);
- // increment, e.g. 1.2999 -> 1.3
- repeat
- case S2[j] of
- '0'..'8':
- begin
- S2[j]:=chr(ord(S2[j])+1);
- break;
- end;
- '9':
- S2[j]:='0';
- '.': ;
- end;
- dec(j);
- if (j=0) or not (S2[j] in ['0'..'9','.']) then
- begin
- // e.g. -9.999 became 0.0
- val(copy(S,i+1,length(S)),Exp,Code);
- if Code=0 then
- begin
- S2:='1E'+IntToStr(Exp+1);
- if S[1]='-' then
- S2:='-'+S2;
- end;
- break;
- end;
- until false;
- val(S2,D,Code);
- if (Code=0) and (D=AsNumber) then
- S:=S2;
- end;
- else
- if s[i-1]='0' then
- begin
- // 1.2340E...
- S2:=LeftStr(S,i-2)+copy(S,i,length(S));
- val(S2,D,Code);
- if (Code=0) and (D=AsNumber) then
- S:=S2;
- end;
- end;
- end;
- // chomp default exponent E+000
- i:=Pos('E',S);
- if i>0 then
- begin
- val(copy(S,i+1,length(S)),Exp,Code);
- if Code=0 then
- begin
- if Exp=0 then
- // 1.1E+000 -> 1.1
- Delete(S,i,length(S))
- else if (Exp>=-6) and (Exp<=6) then
- begin
- // small exponent -> use notation without E
- Delete(S,i,length(S));
- j:=Pos('.',S);
- if j>0 then
- Delete(S,j,1)
- else
- begin
- j:=1;
- while not (S[j] in ['0'..'9']) do inc(j);
- end;
- if Exp<0 then
- begin
- // e.g. -1.2 E-1
- while Exp<0 do
- begin
- if (j>1) and (S[j-1] in ['0'..'9']) then
- dec(j)
- else
- Insert('0',S,j);
- inc(Exp);
- end;
- Insert('.',S,j);
- if (j=1) or not (S[j-1] in ['0'..'9']) then
- Insert('0',S,j);
- if S[length(S)]='0' then
- Delete(S,length(S),1);
- end
- else
- begin
- // e.g. -1.2 E1
- while Exp>0 do
- begin
- if (j<=length(S)) and (S[j] in ['0'..'9']) then
- inc(j)
- else
- Insert('0',S,j);
- dec(Exp);
- end;
- if j<=length(S) then
- Insert('.',S,j);
- end;
- end
- else
- begin
- // e.g. 1.1E+0010 -> 1.1E10
- S:=LeftStr(S,i)+IntToStr(Exp);
- if (i >= 4) and (s[i-1] = '0') and (s[i-2] = '.') then
- // e.g. 1.0E22 -> 1E22
- Delete(S, i-2, 2);
- end
- end;
- end;
- end;
- end;
- jstObject : ;
- jstReference : ;
- jstCompletion : ;
- end;
- if S='' then exit;
- case S[1] of
- '+': if FLastChar='+' then Write(' ');
- '-': if FLastChar='-' then Write(' ');
- end;
- Write(S);
- end;
- constructor TJSWriter.Create(AWriter: TTextWriter);
- begin
- FWriter:=AWriter;
- FIndentChar:=' ';
- FOptions:=[{$ifdef FPC_HAS_CPSTRING}woUseUTF8{$endif}];
- end;
- {$ifdef HasFileWriter}
- constructor TJSWriter.Create(const AFileName: String);
- begin
- Create(TFileWriter.Create(AFileName));
- FFreeWriter:=True;
- end;
- {$endif}
- destructor TJSWriter.Destroy;
- begin
- If FFreeWriter then
- begin
- FWriter.Free;
- FWriter:=Nil;
- end;
- inherited Destroy;
- end;
- procedure TJSWriter.WriteFuncDef(FD: TJSFuncDef);
- Var
- C : Boolean;
- I : Integer;
- A, LastEl: TJSElement;
- begin
- LastEl:=Writer.CurElement;
- C:=(woCompact in Options);
- if fd.IsAsync then
- Write('async ');
- Write('function ');
- If (FD.Name<>'') then
- Write(FD.Name);
- Write('(');
- if Assigned(FD.Params) then
- For I:=0 to FD.Params.Count-1 do
- begin
- write(FD.Params[i]);
- if I<FD.Params.Count-1 then
- if C then Write(',') else Write (', ');
- end;
- Write(') {');
- if Not (C or FD.IsEmpty) then
- begin
- Writeln('');
- Indent;
- end;
- if Assigned(FD.Body) then
- begin
- FSkipCurlyBrackets:=True;
- //writeln('TJSWriter.WriteFuncDef '+FD.Body.ClassName);
- WriteJS(FD.Body);
- A:=FD.Body.A;
- If (Assigned(A))
- and (not (A is TJSStatementList))
- and (not (A is TJSSourceElements))
- and (not (A is TJSEmptyBlockStatement))
- then
- begin
- if FLastChar<>';' then
- Write(';');
- if C then
- Write(' ')
- else
- Writeln('');
- end;
- end;
- Writer.CurElement:=LastEl;
- if C then
- Write('}')
- else
- begin
- Undent;
- Write('}'); // do not writeln
- end;
- end;
- procedure TJSWriter.WriteEmptyBlockStatement(El: TJSEmptyBlockStatement);
- begin
- if El=nil then ;
- if woCompact in Options then
- Write('{}')
- else
- begin
- Writeln('{');
- Write('}');
- end;
- end;
- procedure TJSWriter.WriteEmptyStatement(El: TJSEmptyStatement);
- begin
- if El=nil then ;
- if woEmptyStatementAsComment in Options then
- Write('/* Empty statement */')
- end;
- procedure TJSWriter.WriteRegularExpressionLiteral(
- El: TJSRegularExpressionLiteral);
- begin
- Write('/');
- Write(EscapeString(El.Pattern.AsString,jseqBoth));
- Write('/');
- If Assigned(El.PatternFlags) then
- Write(EscapeString(El.PatternFlags.AsString,jseqBoth));
- end;
- procedure TJSWriter.WriteLiteral(El: TJSLiteral);
- begin
- WriteValue(El.Value);
- end;
- procedure TJSWriter.WritePrimaryExpression(El: TJSPrimaryExpression);
- begin
- if El is TJSPrimaryExpressionThis then
- Write('this')
- else if El is TJSPrimaryExpressionIdent then
- Write(TJSPrimaryExpressionIdent(El).Name)
- else
- Error(SErrUnknownJSClass,[El.ClassName]);
- end;
- procedure TJSWriter.WriteArrayLiteral(El: TJSArrayLiteral);
- type
- BracketString = string{$ifdef fpc}[2]{$endif};
- Var
- Chars : Array[Boolean] of BracketString = ('[]','()');
- Var
- i,C : Integer;
- isArgs,WC , MultiLine: Boolean;
- BC : BracketString;
- begin
- isArgs:=El is TJSArguments;
- BC:=Chars[isArgs];
- C:=El.Elements.Count-1;
- if C=-1 then
- begin
- Write(BC);
- Exit;
- end;
- WC:=(woCompact in Options) or
- ((Not isArgs) and (woCompactArrayLiterals in Options)) or
- (isArgs and (woCompactArguments in Options)) ;
- MultiLine:=(not WC) and (C>4);
- if MultiLine then
- begin
- Writeln(BC[1]);
- Indent;
- end
- else
- Write(BC[1]);
- For I:=0 to C do
- begin
- FSkipRoundBrackets:=true;
- WriteJS(El.Elements[i].Expr);
- if I<C then
- if WC then
- Write(',')
- else if MultiLine then
- Writeln(',')
- else
- Write(', ');
- end;
- if MultiLine then
- begin
- Writeln('');
- Undent;
- end;
- Writer.CurElement:=El;
- Write(BC[2]);
- end;
- procedure TJSWriter.WriteObjectLiteral(El: TJSObjectLiteral);
- Var
- i,C : Integer;
- QE,WC : Boolean;
- S : TJSString;
- Prop: TJSObjectLiteralElement;
- begin
- C:=El.Elements.Count-1;
- QE:=(woQuoteElementNames in Options);
- if C=-1 then
- begin
- Write('{}');
- Exit;
- end;
- WC:=(woCompact in Options) or (woCompactObjectLiterals in Options);
- if WC then
- Write('{')
- else
- begin
- Writeln('{');
- Indent;
- end;
- For I:=0 to C do
- begin
- Prop:=El.Elements[i];
- Writer.CurElement:=Prop.Expr;
- S:=Prop.Name;
- if QE or not IsValidJSIdentifier(S) then
- begin
- if (length(S)>1)
- and (((S[1]='"') and (S[length(S)]='"'))
- or ((S[1]='''') and (S[length(S)]=''''))) then
- // already quoted
- else
- S:=QuoteJSString(s);
- end;
- Write(S+': ');
- Indent;
- FSkipRoundBrackets:=true;
- WriteJS(Prop.Expr);
- if I<C then
- if WC then Write(', ') else Writeln(',');
- Undent;
- end;
- FSkipRoundBrackets:=false;
- if not WC then
- begin
- Writeln('');
- Undent;
- end;
- Writer.CurElement:=El;
- Write('}');
- end;
- procedure TJSWriter.WriteMemberExpression(El: TJSMemberExpression);
- var
- MExpr: TJSElement;
- Args: TJSArguments;
- begin
- if El is TJSNewMemberExpression then
- Write('new ');
- MExpr:=El.MExpr;
- if (MExpr is TJSPrimaryExpression)
- or (MExpr is TJSDotMemberExpression)
- or (MExpr is TJSBracketMemberExpression)
- // Note: new requires brackets in this case: new (a())()
- or ((MExpr is TJSCallExpression) and not (El is TJSNewMemberExpression))
- or (MExpr is TJSLiteral) then
- begin
- WriteJS(MExpr);
- Writer.CurElement:=El;
- end
- else
- begin
- Write('(');
- WriteJS(MExpr);
- Writer.CurElement:=El;
- Write(')');
- end;
- if El is TJSDotMemberExpression then
- begin
- Write('.');
- Write(TJSDotMemberExpression(El).Name);
- end
- else if El is TJSBracketMemberExpression then
- begin
- write('[');
- FSkipRoundBrackets:=true;
- WriteJS(TJSBracketMemberExpression(El).Name);
- Writer.CurElement:=El;
- FSkipRoundBrackets:=false;
- write(']');
- end
- else if (El is TJSNewMemberExpression) then
- begin
- Args:=TJSNewMemberExpression(El).Args;
- if Assigned(Args) then
- begin
- Writer.CurElement:=Args;
- WriteArrayLiteral(Args);
- end
- else
- Write('()');
- end;
- end;
- procedure TJSWriter.WriteCallExpression(El: TJSCallExpression);
- begin
- WriteJS(El.Expr);
- if Assigned(El.Args) then
- begin
- Writer.CurElement:=El.Args;
- WriteArrayLiteral(El.Args);
- end
- else
- begin
- Writer.CurElement:=El;
- Write('()');
- end;
- end;
- procedure TJSWriter.WriteUnary(El: TJSUnary);
- Var
- S : String;
- begin
- FSkipRoundBrackets:=false;
- S:=El.PreFixOperator;
- if (S<>'') then
- begin
- case S[1] of
- '+': if FLastChar='+' then Write(' ');
- '-': if FLastChar='-' then Write(' ');
- end;
- Write(S);
- end;
- WriteJS(El.A);
- S:=El.PostFixOperator;
- if (S<>'') then
- begin
- Writer.CurElement:=El;
- case S[1] of
- '+': if FLastChar='+' then Write(' ');
- '-': if FLastChar='-' then Write(' ');
- end;
- Write(S);
- end;
- end;
- procedure TJSWriter.WriteStatementList(El: TJSStatementList);
- Var
- C : Boolean;
- LastEl: TJSElement;
- ElStack: array of TJSElement;
- ElStackIndex: integer;
- procedure WriteNonListEl(CurEl: TJSElement);
- begin
- if IsEmptyStatement(CurEl) then exit;
- if (LastEl<>nil) then
- begin
- if FLastChar<>';' then
- Write(';');
- if C then
- Write(' ')
- else
- Writeln('');
- end;
- WriteJS(CurEl);
- LastEl:=CurEl;
- end;
- procedure Push(CurEl: TJSElement);
- begin
- if CurEl=nil then exit;
- if ElStackIndex=length(ElStack) then
- SetLength(ElStack,ElStackIndex+8);
- ElStack[ElStackIndex]:=CurEl;
- inc(ElStackIndex);
- end;
- function Pop: TJSElement;
- begin
- if ElStackIndex=0 then exit(nil);
- dec(ElStackIndex);
- Result:=ElStack[ElStackIndex];
- end;
- var
- B : Boolean;
- CurEl: TJSElement;
- List: TJSStatementList;
- begin
- //write('TJSWriter.WriteStatementList '+BoolToStr(FSkipCurlyBrackets,true));
- //if El.A<>nil then write(' El.A='+El.A.ClassName) else write(' El.A=nil');
- //if El.B<>nil then write(' El.B='+El.B.ClassName) else write(' El.B=nil');
- //writeln(' ');
- C:=(woCompact in Options);
- B:= Not FSkipCurlyBrackets;
- FSkipCurlyBrackets:=True;
- if B then
- begin
- Write('{');
- Indent;
- if not C then writeln('');
- end;
- // traverse statementlist using a heap stack to avoid large stack depths
- LastEl:=nil;
- ElStackIndex:=0;
- CurEl:=El;
- while CurEl<>nil do
- begin
- if CurEl is TJSStatementList then
- begin
- List:=TJSStatementList(CurEl);
- if List.A is TJSStatementList then
- begin
- Push(List.B);
- CurEl:=List.A;
- end
- else
- begin
- WriteNonListEl(List.A);
- if List.B is TJSStatementList then
- CurEl:=List.B
- else
- begin
- WriteNonListEl(List.B);
- CurEl:=nil;
- end;
- end;
- end
- else
- begin
- WriteNonListEl(CurEl);
- CurEl:=nil;
- end;
- if CurEl=nil then
- CurEl:=Pop;
- end;
- if (LastEl<>nil) and not C then
- if FLastChar=';' then
- writeln('')
- else
- writeln(';');
- if B then
- begin
- Undent;
- Writer.CurElement:=El;
- Write('}'); // do not writeln
- end;
- end;
- procedure TJSWriter.WriteWithStatement(El: TJSWithStatement);
- begin
- Write('with (');
- FSkipRoundBrackets:=true;
- WriteJS(El.A);
- FSkipRoundBrackets:=false;
- Writer.CurElement:=El;
- if (woCompact in Options) then
- Write(') ')
- else
- WriteLn(')');
- Indent;
- WriteJS(El.B);
- Undent;
- end;
- procedure TJSWriter.WriteVarDeclarationList(El: TJSVariableDeclarationList);
- begin
- WriteJS(El.A);
- If Assigned(El.B) then
- begin
- Write(', ');
- WriteJS(El.B);
- end;
- end;
- procedure TJSWriter.WriteBinary(El: TJSBinary);
- var
- ElC: TClass;
- S : String;
- procedure WriteRight(Bin: TJSBinary);
- begin
- FSkipRoundBrackets:=(Bin.B.ClassType=ElC)
- and ((ElC=TJSLogicalOrExpression)
- or (ElC=TJSLogicalAndExpression));
- Write(S);
- WriteJS(Bin.B);
- Writer.CurElement:=Bin;
- end;
- Var
- AllowCompact, WithBrackets: Boolean;
- Left: TJSElement;
- SubBin: TJSBinaryExpression;
- Binaries: TJSElementArray;
- BinariesCnt: integer;
- begin
- {$IFDEF VerboseJSWriter}
- System.writeln('TJSWriter.WriteBinary SkipRoundBrackets=',FSkipRoundBrackets);
- {$ENDIF}
- WithBrackets:=not FSkipRoundBrackets;
- if WithBrackets then
- Write('(');
- FSkipRoundBrackets:=false;
- ElC:=El.ClassType;
- Left:=El.A;
- AllowCompact:=False;
- S:='';
- if (El is TJSBinaryExpression) then
- begin
- S:=TJSBinaryExpression(El).OperatorString;
- AllowCompact:=TJSBinaryExpression(El).AllowCompact;
- end;
- If Not (AllowCompact and (woCompact in Options)) then
- begin
- if El is TJSCommaExpression then
- S:=S+' '
- else
- S:=' '+S+' ';
- end;
- if (Left is TJSBinaryExpression)
- and (Left.ClassType=ElC)
- and ((ElC=TJSLogicalOrExpression)
- or (ElC=TJSLogicalAndExpression)
- or (ElC=TJSBitwiseAndExpression)
- or (ElC=TJSBitwiseOrExpression)
- or (ElC=TJSBitwiseXOrExpression)
- or (ElC=TJSAdditiveExpressionPlus)
- or (ElC=TJSAdditiveExpressionMinus)
- or (ElC=TJSMultiplicativeExpressionMul)) then
- begin
- // handle left handed multi add without stack
- SetLength(Binaries{%H-},8);
- BinariesCnt:=0;
- while Left is TJSBinaryExpression do
- begin
- SubBin:=TJSBinaryExpression(Left);
- if SubBin.ClassType<>ElC then break;
- if BinariesCnt=length(Binaries) then
- SetLength(Binaries,BinariesCnt*2);
- Binaries[BinariesCnt]:=SubBin;
- inc(BinariesCnt);
- Left:=SubBin.A;
- end;
- WriteJS(Left);
- Writer.CurElement:=El;
- while BinariesCnt>0 do
- begin
- dec(BinariesCnt);
- WriteRight(TJSBinaryExpression(Binaries[BinariesCnt]));
- end;
- end
- else
- begin;
- WriteJS(Left);
- Writer.CurElement:=El;
- end;
- WriteRight(El);
- if WithBrackets then
- Write(')');
- end;
- function TJSWriter.IsEmptyStatement(El: TJSElement): boolean;
- begin
- if (El=nil) then
- exit(true);
- if (El.ClassType=TJSEmptyStatement) and not (woEmptyStatementAsComment in Options) then
- exit(true);
- Result:=false;
- end;
- function TJSWriter.HasLineEnding(El: TJSElement): boolean;
- begin
- if El<>nil then
- begin
- if (El.ClassType=TJSStatementList) or (El.ClassType=TJSSourceElements) then
- exit(true);
- end;
- Result:=false;
- end;
- procedure TJSWriter.WriteConditionalExpression(El: TJSConditionalExpression);
- var
- NeedBrackets: Boolean;
- begin
- NeedBrackets:=true;
- if NeedBrackets then
- begin
- write('(');
- FSkipRoundBrackets:=true;
- end;
- WriteJS(El.A);
- write(' ? ');
- if El.B<>nil then
- WriteJS(El.B);
- write(' : ');
- if El.C<>nil then
- WriteJS(El.C);
- if NeedBrackets then
- write(')');
- end;
- procedure TJSWriter.WriteAssignStatement(El: TJSAssignStatement);
- Var
- S : String;
- begin
- WriteJS(El.LHS);
- Writer.CurElement:=El;
- S:=El.OperatorString;
- If Not (woCompact in Options) then
- S:=' '+S+' ';
- Write(S);
- FSkipRoundBrackets:=true;
- WriteJS(El.Expr);
- FSkipRoundBrackets:=false;
- end;
- procedure TJSWriter.WriteVarDeclaration(El: TJSVarDeclaration);
- begin
- Write(El.Name);
- if Assigned(El.Init) then
- begin
- Write(' = ');
- FSkipRoundBrackets:=true;
- WriteJS(El.Init);
- FSkipRoundBrackets:=false;
- end;
- end;
- procedure TJSWriter.WriteIfStatement(El: TJSIfStatement);
- var
- HasBTrue, C, HasBFalse, BTrueNeedBrackets: Boolean;
- begin
- C:=woCompact in Options;
- Write('if (');
- FSkipRoundBrackets:=true;
- WriteJS(El.Cond);
- Writer.CurElement:=El;
- FSkipRoundBrackets:=false;
- Write(')');
- If Not C then
- Write(' ');
- HasBTrue:=not IsEmptyStatement(El.BTrue);
- HasBFalse:=not IsEmptyStatement(El.BFalse);
- if HasBTrue then
- begin
- // Note: the 'else' needs {} in front
- BTrueNeedBrackets:=HasBFalse and not (El.BTrue is TJSStatementList)
- and not (El.BTrue is TJSEmptyBlockStatement);
- if BTrueNeedBrackets then
- if C then
- Write('{')
- else
- begin
- Writeln('{');
- Indent;
- end;
- WriteJS(El.BTrue);
- if BTrueNeedBrackets then
- if C then
- Write('}')
- else
- begin
- Undent;
- Writeln('}');
- end;
- end;
- if HasBFalse then
- begin
- Writer.CurElement:=El.BFalse;
- if not HasBTrue then
- begin
- if C then
- Write('{}')
- else
- Writeln('{}');
- end
- else
- Write(' ');
- Write('else ');
- WriteJS(El.BFalse)
- end
- else
- Writer.CurElement:=El;
- end;
- procedure TJSWriter.WriteForInStatement(El: TJSForInStatement);
- begin
- Write('for (');
- if Assigned(El.LHS) then
- begin
- WriteJS(El.LHS);
- Writer.CurElement:=El;
- end;
- Write(' in ');
- if Assigned(El.List) then
- begin
- WriteJS(El.List);
- Writer.CurElement:=El;
- end;
- Write(') ');
- if Assigned(El.Body) then
- WriteJS(El.Body);
- end;
- procedure TJSWriter.WriteForStatement(El: TJSForStatement);
- begin
- Write('for (');
- if Assigned(El.Init) then
- WriteJS(El.Init);
- Write('; ');
- if Assigned(El.Cond) then
- begin
- FSkipRoundBrackets:=true;
- WriteJS(El.Cond);
- FSkipRoundBrackets:=false;
- end;
- Write('; ');
- if Assigned(El.Incr) then
- WriteJS(El.Incr);
- Writer.CurElement:=El;
- Write(') ');
- if Assigned(El.Body) then
- WriteJS(El.Body);
- end;
- procedure TJSWriter.WriteWhileStatement(El: TJSWhileStatement);
- begin
- if El is TJSDoWhileStatement then
- begin
- Write('do ');
- if Assigned(El.Body) then
- begin
- FSkipCurlyBrackets:=false;
- WriteJS(El.Body);
- Writer.CurElement:=El;
- end;
- Write(' while (');
- If Assigned(El.Cond) then
- begin
- FSkipRoundBrackets:=true;
- WriteJS(EL.Cond);
- Writer.CurElement:=El;
- FSkipRoundBrackets:=false;
- end;
- Write(')');
- end
- else
- begin
- Write('while (');
- If Assigned(El.Cond) then
- begin
- FSkipRoundBrackets:=true;
- WriteJS(EL.Cond);
- Writer.CurElement:=El;
- FSkipRoundBrackets:=false;
- end;
- Write(') ');
- if Assigned(El.Body) then
- WriteJS(El.Body);
- end;
- end;
- procedure TJSWriter.WriteSwitchStatement(El: TJSSwitchStatement);
- Var
- C : Boolean;
- I : Integer;
- EC : TJSCaseElement;
- begin
- C:=(woCompact in Options);
- Write('switch (');
- If Assigned(El.Cond) then
- begin
- FSkipRoundBrackets:=true;
- WriteJS(El.Cond);
- Writer.CurElement:=El;
- FSkipRoundBrackets:=false;
- end;
- if C then
- Write(') {')
- else
- Writeln(') {');
- For I:=0 to El.Cases.Count-1 do
- begin
- EC:=El.Cases[i];
- if EC=El.TheDefault then
- Write('default')
- else
- begin
- Writer.CurElement:=EC.Expr;
- Write('case ');
- FSkipRoundBrackets:=true;
- WriteJS(EC.Expr);
- FSkipRoundBrackets:=false;
- end;
- if Assigned(EC.Body) then
- begin
- FSkipCurlyBrackets:=true;
- If C then
- Write(': ')
- else
- Writeln(':');
- Indent;
- WriteJS(EC.Body);
- Undent;
- if (EC.Body is TJSStatementList) or (EC.Body is TJSEmptyBlockStatement) then
- begin
- if C then
- begin
- if I<El.Cases.Count-1 then
- Write(' ');
- end
- else
- Writeln('');
- end
- else if C then
- Write('; ')
- else
- Writeln(';');
- end
- else
- begin
- if C then
- Write(': ')
- else
- Writeln(':');
- end;
- end;
- Writer.CurElement:=El;
- Write('}');
- end;
- procedure TJSWriter.WriteTargetStatement(El: TJSTargetStatement);
- Var
- TN : TJSString;
- begin
- TN:=El.TargetName;
- if (El is TJSForStatement) then
- WriteForStatement(TJSForStatement(El))
- else if (El is TJSSwitchStatement) then
- WriteSwitchStatement(TJSSwitchStatement(El))
- else if (El is TJSForInStatement) then
- WriteForInStatement(TJSForInStatement(El))
- else if El is TJSWhileStatement then
- WriteWhileStatement(TJSWhileStatement(El))
- else if (El is TJSContinueStatement) then
- begin
- if (TN<>'') then
- Write('continue '+TN)
- else
- Write('continue');
- end
- else if (El is TJSBreakStatement) then
- begin
- if (TN<>'') then
- Write('break '+TN)
- else
- Write('break');
- end
- else
- Error('Unknown target statement class: "%s"',[El.ClassName])
- end;
- procedure TJSWriter.WriteReturnStatement(El: TJSReturnStatement);
- begin
- if El.Expr=nil then
- Write('return')
- else
- begin
- Write('return ');
- FSkipRoundBrackets:=true;
- WriteJS(El.Expr);
- FSkipRoundBrackets:=false;
- end;
- end;
- procedure TJSWriter.WriteLabeledStatement(El: TJSLabeledStatement);
- begin
- if Assigned(El.TheLabel) then
- begin
- Write(El.TheLabel.Name);
- if woCompact in Options then
- Write(': ')
- else
- Writeln(':');
- end;
- // Target ??
- WriteJS(El.A);
- end;
- procedure TJSWriter.WriteTryStatement(El: TJSTryStatement);
- Var
- C : Boolean;
- begin
- C:=woCompact in Options;
- Write('try {');
- if not IsEmptyStatement(El.Block) then
- begin
- if Not C then writeln('');
- FSkipCurlyBrackets:=True;
- Indent;
- WriteJS(El.Block);
- if (Not C) and (not (El.Block is TJSStatementList)) then writeln('');
- Undent;
- end;
- Writer.CurElement:=El;
- Write('}');
- If (El is TJSTryCatchFinallyStatement) or (El is TJSTryCatchStatement) then
- begin
- Write(' catch');
- if El.Ident<>'' then Write(' ('+El.Ident+')');
- If C then
- Write(' {')
- else
- Writeln(' {');
- if not IsEmptyStatement(El.BCatch) then
- begin
- FSkipCurlyBrackets:=True;
- Indent;
- WriteJS(El.BCatch);
- Undent;
- if (Not C) and (not (El.BCatch is TJSStatementList)) then writeln('');
- end;
- Writer.CurElement:=El;
- Write('}');
- end;
- If (El is TJSTryCatchFinallyStatement) or (El is TJSTryFinallyStatement) then
- begin
- If C then
- Write(' finally {')
- else
- Writeln(' finally {');
- if not IsEmptyStatement(El.BFinally) then
- begin
- Indent;
- FSkipCurlyBrackets:=True;
- WriteJS(El.BFinally);
- Undent;
- if (Not C) and (not (El.BFinally is TJSStatementList)) then writeln('');
- end;
- Writer.CurElement:=El;
- Write('}');
- end;
- end;
- procedure TJSWriter.WriteFunctionBody(El: TJSFunctionBody);
- begin
- //writeln('TJSWriter.WriteFunctionBody '+El.A.ClassName+' FSkipBrackets='+BoolToStr(FSkipCurlyBrackets,'true','false'));
- if not IsEmptyStatement(El.A) then
- WriteJS(El.A);
- end;
- procedure TJSWriter.WriteFunctionDeclarationStatement(
- El: TJSFunctionDeclarationStatement);
- begin
- if Assigned(El.AFunction) then
- WriteFuncDef(El.AFunction);
- end;
- procedure TJSWriter.WriteSourceElements(El: TJSSourceElements);
- Var
- C : Boolean;
- Procedure WriteElements(Elements: TJSElementNodes);
- Var
- I : Integer;
- E : TJSElement;
- begin
- if Elements=nil then exit;
- For I:=0 to Elements.Count-1 do
- begin
- E:=Elements.Nodes[i].Node;
- WriteJS(E);
- if Not C then
- WriteLn(';')
- else
- if I<Elements.Count-1 then
- Write('; ')
- else
- Write(';')
- end;
- end;
- begin
- C:=(woCompact in Options);
- WriteElements(El.Vars);
- WriteElements(El.Functions);
- WriteElements(El.Statements);
- end;
- procedure TJSWriter.WriteVariableStatement(El: TJSVariableStatement);
- begin
- Write('var ');
- FSkipRoundBrackets:=true;
- WriteJS(El.A);
- end;
- procedure TJSWriter.WriteJS(El: TJSElement);
- var
- C: TClass;
- begin
- {$IFDEF DEBUGJSWRITER}
- if (El<>Nil) then
- system.Writeln('WriteJS : ',El.ClassName,' ',El.Line,',',El.Column)
- else
- system.Writeln('WriteJS : El = Nil');
- {$ENDIF}
- Writer.CurElement:=El;
- C:=El.ClassType;
- if (C=TJSEmptyBlockStatement ) then
- WriteEmptyBlockStatement(TJSEmptyBlockStatement(El))
- else if (C=TJSEmptyStatement) then
- WriteEmptyStatement(TJSEmptyStatement(El))
- else if (C=TJSLiteral) then
- WriteLiteral(TJSLiteral(El))
- else if C.InheritsFrom(TJSPrimaryExpression) then
- WritePrimaryExpression(TJSPrimaryExpression(El))
- else if C.InheritsFrom(TJSArrayLiteral) then
- WriteArrayLiteral(TJSArrayLiteral(El))
- else if (C=TJSObjectLiteral) then
- WriteObjectLiteral(TJSObjectLiteral(El))
- else if C.InheritsFrom(TJSMemberExpression) then
- WriteMemberExpression(TJSMemberExpression(El))
- else if (C=TJSRegularExpressionLiteral) then
- WriteRegularExpressionLiteral(TJSRegularExpressionLiteral(El))
- else if (C=TJSCallExpression) then
- WriteCallExpression(TJSCallExpression(El))
- else if (C=TJSLabeledStatement) then // Before unary
- WriteLabeledStatement(TJSLabeledStatement(El))
- else if (C=TJSFunctionBody) then // Before unary
- WriteFunctionBody(TJSFunctionBody(El))
- else if (C=TJSVariableStatement) then // Before unary
- WriteVariableStatement(TJSVariableStatement(El))
- else if C.InheritsFrom(TJSUnary) then
- WriteUnary(TJSUnary(El))
- else if (C=TJSVariableDeclarationList) then
- WriteVarDeclarationList(TJSVariableDeclarationList(El)) // Must be before binary
- else if (C=TJSStatementList) then
- WriteStatementList(TJSStatementList(El)) // Must be before binary
- else if (C=TJSWithStatement) then
- WriteWithStatement(TJSWithStatement(El)) // Must be before binary
- else if C.InheritsFrom(TJSBinary) then
- WriteBinary(TJSBinary(El))
- else if (C=TJSConditionalExpression) then
- WriteConditionalExpression(TJSConditionalExpression(El))
- else if C.InheritsFrom(TJSAssignStatement) then
- WriteAssignStatement(TJSAssignStatement(El))
- else if (C=TJSVarDeclaration) then
- WriteVarDeclaration(TJSVarDeclaration(El))
- else if (C=TJSIfStatement) then
- WriteIfStatement(TJSIfStatement(El))
- else if C.InheritsFrom(TJSTargetStatement) then
- WriteTargetStatement(TJSTargetStatement(El))
- else if (C=TJSReturnStatement) then
- WriteReturnStatement(TJSReturnStatement(El))
- else if C.InheritsFrom(TJSTryStatement) then
- WriteTryStatement(TJSTryStatement(El))
- else if (C=TJSFunctionDeclarationStatement) then
- WriteFunctionDeclarationStatement(TJSFunctionDeclarationStatement(El))
- else if (C=TJSSourceElements) then
- WriteSourceElements(TJSSourceElements(El))
- else if El=Nil then
- Error(SErrNilNode)
- else
- Error(SErrUnknownJSClass,[El.ClassName]);
- // Write('/* '+El.ClassName+' */');
- FSkipCurlyBrackets:=False;
- end;
- {AllowWriteln-}
- {$ifdef HasFileWriter}
- { TFileWriter }
- Function TFileWriter.DoWrite(Const S: TJSWriterString) : Integer;
- begin
- Result:=Length(S);
- {$ifdef NodeJS}
- system.writeln('TFileWriter.DoWrite ToDo ',S);
- {$else}
- system.Write(FFile,S);
- {$endif}
- end;
- {$ifdef FPC_HAS_CPSTRING}
- Function TFileWriter.DoWrite(Const S: UnicodeString) : Integer;
- begin
- Result:=Length(S)*SizeOf(UnicodeChar);
- system.Write(FFile,S);
- end;
- {$endif}
- Constructor TFileWriter.Create(Const AFileName: String);
- begin
- inherited Create;
- FFileName:=AFileName;
- {$ifdef NodeJS}
- system.writeln('TFileWriter.Create ToDo ',AFileName);
- {$else}
- Assign(FFile,AFileName);
- Rewrite(FFile);
- {$endif}
- end;
- Destructor TFileWriter.Destroy;
- begin
- Close;
- Inherited;
- end;
- Procedure TFileWriter.Flush;
- begin
- {$ifdef NodeJS}
- system.writeln('TFileWriter.Flush ToDO');
- {$else}
- system.Flush(FFile);
- {$endif}
- end;
- Procedure TFileWriter.Close;
- begin
- {$ifdef NodeJS}
- system.writeln('TFileWriter.DoWrite ToDo ');
- {$else}
- system.Close(FFile);
- {$endif}
- end;
- {$endif}
- { TTextWriter }
- procedure TTextWriter.SetCurElement(const AValue: TJSElement);
- begin
- FCurElement:=AValue;
- end;
- procedure TTextWriter.Writing;
- begin
- if Assigned(OnWriting) then
- OnWriting(Self);
- end;
- constructor TTextWriter.Create;
- begin
- FCurLine:=1;
- FCurColumn:=1;
- FLineBreak:=sLineBreak;
- end;
- {$ifdef FPC_HAS_CPSTRING}
- function TTextWriter.Write(const S: UnicodeString): Integer;
- var
- p: PWideChar;
- c: WideChar;
- begin
- if S='' then exit;
- Writing;
- Result:=DoWrite(S);
- p:=PWideChar(S);
- repeat
- c:=p^;
- case c of
- #0:
- if p-PWideChar(S)=length(S)*2 then
- break
- else
- inc(FCurColumn);
- #10,#13:
- begin
- FCurColumn:=1;
- inc(FCurLine);
- inc(p);
- if (p^ in [#10,#13]) and (c<>p^) then inc(p);
- continue;
- end;
- else
- // ignore low/high surrogate, CurColumn is char index, not codepoint
- inc(FCurColumn);
- end;
- inc(p);
- until false;
- end;
- {$endif}
- function TTextWriter.Write(const S: TJSWriterString): Integer;
- var
- c: Char;
- l, p: Integer;
- begin
- if S='' then exit;
- Writing;
- Result:=DoWrite(S);
- l:=length(S);
- p:=1;
- while p<=l do
- begin
- c:=S[p];
- case c of
- #10,#13:
- begin
- FCurColumn:=1;
- inc(FCurLine);
- inc(p);
- if (p<=l) and (S[p] in [#10,#13]) and (c<>S[p]) then inc(p);
- end;
- else
- // Note about UTF-8 multibyte chars: CurColumn is char index, not codepoint
- inc(FCurColumn);
- inc(p);
- end;
- end;
- end;
- function TTextWriter.WriteLn(const S: TJSWriterString): Integer;
- begin
- Result:=Write(S)+Write(LineBreak);
- end;
- function TTextWriter.Write(const Fmt: TJSWriterString;
- Args: array of const): Integer;
- begin
- Result:=Write(Format(Fmt,Args));
- end;
- function TTextWriter.WriteLn(const Fmt: TJSWriterString;
- Args: array of const): Integer;
- begin
- Result:=WriteLn(Format(Fmt,Args));
- end;
- function TTextWriter.Write(const Args: array of const): Integer;
- Var
- I : Integer;
- {$ifdef pas2js}
- V: jsvalue;
- S: TJSWriterString;
- {$else}
- V : TVarRec;
- S : String;
- U : UnicodeString;
- {$endif}
- begin
- Result:=0;
- For I:=Low(Args) to High(Args) do
- begin
- V:=Args[i];
- S:='';
- {$ifdef pas2js}
- case jsTypeOf(V) of
- 'boolean':
- if V then S:='true' else S:='false';
- 'number':
- if isInteger(V) then
- S:=str(NativeInt(V))
- else
- S:=str(Double(V));
- 'string':
- S:=String(V);
- else continue;
- end;
- Result:=Result+Write(S);
- {$else}
- U:='';
- case V.VType of
- vtInteger : Str(V.VInteger,S);
- vtBoolean : if V.VBoolean then s:='true' else s:='false';
- vtChar : s:=V.VChar;
- vtWideChar : U:=V.VWideChar;
- vtExtended : Str(V.VExtended^,S);
- vtString : S:=V.VString^;
- vtPChar : S:=V.VPChar;
- vtPWideChar : U:=V.VPWideChar;
- vtAnsiString : S:=PChar(V.VAnsiString);
- vtCurrency : Str(V.VCurrency^,S);
- vtVariant : S:=V.VVariant^;
- vtWideString : U:=PWideChar(V.VWideString);
- vtInt64 : Str(V.VInt64^,S);
- vtUnicodeString : U:=PWideChar(V.VUnicodeString);
- vtQWord : Str(V.VQWord^,S);
- end;
- if (U<>'') then
- Result:=Result+Write(u)
- else if (S<>'') then
- Result:=Result+Write(s);
- {$endif}
- end;
- end;
- function TTextWriter.WriteLn(const Args: array of const): Integer;
- begin
- Result:=Write(Args)+Writeln('');
- end;
- end.
|