Преглед изворни кода

* Javascript writer

git-svn-id: trunk@27035 -
michael пре 11 година
родитељ
комит
0c8f3d6c12

+ 2 - 0
.gitattributes

@@ -2362,8 +2362,10 @@ packages/fcl-js/src/jsparser.pp svneol=native#text/plain
 packages/fcl-js/src/jsscanner.pp svneol=native#text/plain
 packages/fcl-js/src/jstoken.pp svneol=native#text/plain
 packages/fcl-js/src/jstree.pp svneol=native#text/plain
+packages/fcl-js/src/jswriter.pp svneol=native#text/plain
 packages/fcl-js/tests/tcparser.pp svneol=native#text/plain
 packages/fcl-js/tests/tcscanner.pp svneol=native#text/plain
+packages/fcl-js/tests/tcwriter.pp svneol=native#text/plain
 packages/fcl-js/tests/testjs.ico -text
 packages/fcl-js/tests/testjs.lpi svneol=native#text/plain
 packages/fcl-js/tests/testjs.lpr svneol=native#text/plain

+ 1248 - 0
packages/fcl-js/src/jswriter.pp

@@ -0,0 +1,1248 @@
+unit jswriter;
+
+{$mode objfpc}{$H+}
+{ $DEFINE DEBUGJSWRITER}
+
+interface
+
+uses
+  {Classes, } SysUtils, jstoken, jsbase, jstree;
+
+Type
+
+  { TTextWriter }
+
+  TTextWriter = Class(TObject)
+  protected
+    Function DoWrite(Const S : AnsiString) : Integer; virtual; abstract;
+    Function DoWrite(Const S : UnicodeString) : Integer; virtual; abstract;
+  Public
+    // All functions return the numberof bytes copied to output stream.
+    Function Write(Const S : UnicodeString) : Integer;
+    Function Write(Const S : AnsiString) : Integer;
+    Function WriteLn(Const S : AnsiString) : Integer;
+    Function Write(Const Fmt : AnsiString; Args : Array of const) : Integer;
+    Function WriteLn(Const Fmt : AnsiString; Args : Array of const) : Integer;
+    Function Write(Const Args : Array of const) : Integer;
+    Function WriteLn(Const Args : Array of const) : Integer;
+  end;
+
+  { TFileWriter }
+
+  TFileWriter = Class(TTextWriter)
+  Protected
+    FFile : Text;
+    FFileName : String;
+    Function DoWrite(Const S : AnsiString) : Integer; override;
+    Function DoWrite(Const S : UnicodeString) : Integer; override;
+  Public
+    Constructor Create(Const AFileNAme : String);
+    Destructor Destroy; override;
+    Procedure Flush;
+    Procedure Close;
+    Property FileName : String Read FFileName;
+  end;
+
+  { TBufferWriter }
+  TBytes = Array of byte;
+  TBufferWriter = Class(TTextWriter)
+  private
+    FBufPos,
+    FCapacity: Cardinal;
+    FBuffer : TBytes;
+    function GetAsAnsistring: AnsiString;
+    function GetBuffer: Pointer;
+    function GetBufferLength: Integer;
+    function GetCapacity: Cardinal;
+    function GetUnicodeString: UnicodeString;
+    procedure SetCapacity(AValue: Cardinal);
+  Protected
+    Function DoWrite(Const S : AnsiString) : integer; override;
+    Function DoWrite(Const S : UnicodeString) : integer; override;
+  Public
+    Constructor Create(Const ACapacity : Cardinal);
+    Procedure SaveToFile(Const AFileName : String);
+    Property Buffer : Pointer Read GetBuffer;
+    Property BufferLength : Integer Read GetBufferLength;
+    Property Capacity : Cardinal Read GetCapacity Write SetCapacity;
+    Property AsAnsistring : AnsiString Read GetAsAnsistring;
+    Property AsUnicodeString : UnicodeString Read GetUnicodeString;
+  end;
+
+
+  { TJSWriter }
+  TWriteOption = (woCompact,
+                  woUseUTF8,
+                  woTabIndent,
+                  woEmptyStatementAsComment,
+                  woQuoteElementNames);
+  TWriteOptions = Set of TWriteOption;
+
+  TJSWriter = Class
+  private
+    FCurIndent : Integer;
+    FLinePos : Integer;
+    FIndentSize: Byte;
+    FIndentChar : Char;
+    FOptions: TWriteOptions;
+    FWriter: TTextWriter;
+    FFreeWriter : Boolean;
+    FSkipBrackets : Boolean;
+    function GetUseUTF8: Boolean;
+    procedure SetOptions(AValue: TWriteOptions);
+  Protected
+    // Helper routines
+    Procedure Error(Const Msg : String);
+    Procedure Error(Const Fmt : String; Args : Array of const);
+    Procedure WriteIndent; // inline;
+    Procedure Write(Const U : UnicodeString);
+    Procedure Write(Const S : AnsiString);
+    Procedure WriteLn(Const S : AnsiString);
+    Procedure WriteLn(Const U : UnicodeString);
+    // 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;
+  Public
+    Class Function EscapeString(const S: TJSString): TJSString;
+    Constructor Create(AWriter : TTextWriter);
+    Constructor Create(Const AFileName : String);
+    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;
+  end;
+  EJSWriter = CLass(Exception);
+
+implementation
+
+Resourcestring
+  SErrUnknownJSClass = 'Unknown javascript element class : %s';
+
+{ TBufferWriter }
+
+function TBufferWriter.GetBufferLength: Integer;
+begin
+  Result:=FBufPos;
+end;
+
+function TBufferWriter.GetAsAnsistring: AnsiString;
+begin
+  SetLength(Result,BufferLength);
+  if (BufferLength>0) then
+    Move(FBuffer[0],Result[1],BufferLength);
+end;
+
+function TBufferWriter.GetBuffer: Pointer;
+begin
+  Result:=Pointer(FBuffer);
+end;
+
+function TBufferWriter.GetCapacity: Cardinal;
+begin
+  Result:=Length(FBuffer);
+end;
+
+function TBufferWriter.GetUnicodeString: UnicodeString;
+
+Var
+  SL : Integer;
+
+begin
+  SL:=BufferLength div SizeOf(UnicodeChar); // Silently ignores last byte
+  SetLength(Result,SL);
+  if (SL>0) then
+    Move(FBuffer[0],Result[1],SL*SizeOf(UnicodeChar));
+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: AnsiString): integer;
+
+Var
+  DesLen,MinLen : Integer;
+
+begin
+  Result:=Length(S)*SizeOf(Char);
+  MinLen:=Result+FBufPos;
+  If (MinLen>Capacity) then
+    begin
+    DesLen:=Round(FCapacity*1.25);
+    if DesLen>MinLen then
+      MinLen:=DesLen;
+    Capacity:=MinLen;
+    end;
+  Move(S[1],FBuffer[FBufPos],Result);
+  FBufPos:=FBufPos+Result;
+end;
+
+Function TBufferWriter.DoWrite(Const S: UnicodeString): integer;
+
+Var
+  DesLen,MinLen : Integer;
+
+begin
+  Result:=Length(S)*SizeOf(UnicodeChar);
+  MinLen:=Result+FBufPos;
+  If (MinLen>Capacity) then
+    begin
+    DesLen:=Round(FCapacity*1.25);
+    if DesLen>MinLen then
+      MinLen:=DesLen;
+    Capacity:=MinLen;
+    end;
+  Move(S[1],FBuffer[FBufPos],Result);
+  FBufPos:=FBufPos+Result;
+end;
+
+Constructor TBufferWriter.Create(Const ACapacity: Cardinal);
+begin
+  Capacity:=ACapacity;
+end;
+
+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;
+
+{ TJSWriter }
+
+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:=(woUseUTF8 in Options)
+end;
+
+Procedure TJSWriter.Error(Const Msg: String);
+begin
+  Raise EJSWriter.Create(Msg);
+end;
+
+Procedure TJSWriter.Error(Const Fmt: String; Args: Array of const);
+begin
+  Raise EJSWriter.CreateFmt(Fmt,Args);
+end;
+
+Procedure TJSWriter.WriteIndent;
+
+begin
+  If (FLinePos=0) then
+    FLinePos:=Writer.Write(StringOfChar(FIndentChar,FCurIndent));
+end;
+
+Procedure TJSWriter.Indent;
+begin
+  Inc(FCurIndent,FIndentSIze);
+end;
+
+Procedure TJSWriter.Undent;
+begin
+  if (FCurIndent>=FIndentSIze) then
+    Dec(FCurIndent,FIndentSIze)
+  else
+    FCurIndent:=0;
+end;
+
+Procedure TJSWriter.Write(Const U: UnicodeString);
+
+Var
+  S : UTF8String;
+
+begin
+  WriteIndent;
+  if UseUTF8 then
+    begin
+    S:=UTF8Encode(U);
+    FLinePos:=FLinePos+Writer.Write(S);
+    end
+  else
+    FLinePos:=FLinePos+Writer.Write(U);
+end;
+
+Procedure TJSWriter.Write(Const S: AnsiString);
+begin
+  if Not (woUseUTF8 in Options) then
+    Write(UnicodeString(S))
+  else
+    begin
+    WriteIndent;
+    FLinePos:=FLinePos+Writer.Write(S);
+    end;
+end;
+
+Procedure TJSWriter.WriteLn(Const S: AnsiString);
+begin
+  if Not (woUseUTF8 in Options) then
+    Writeln(UnicodeString(S))
+  else
+    begin
+    WriteIndent;
+    Writer.WriteLn(S);
+    FLinePos:=0;
+    end;
+end;
+
+Procedure TJSWriter.WriteLn(Const U: UnicodeString);
+Var
+  S : UTF8String;
+
+begin
+  if UseUTF8 then
+    begin
+    S:=UTF8Encode(U);
+    Writeln(S);
+    end
+  else
+    begin
+    WriteIndent;
+    FLinePos:=FLinePos+Writer.Write(U);
+    FLinePos:=0;
+    end;
+end;
+
+Class Function TJSWriter.EscapeString(const S : TJSString) : TJSString;
+
+Var
+  I,J,L : Integer;
+  P : PWideChar;
+
+begin
+  I:=1;
+  J:=1;
+  Result:='';
+  L:=Length(S);
+  P:=PWideChar(S);
+  While I<=L do
+    begin
+    if (AnsiChar(P^) in ['"','/','\',#8,#9,#10,#12,#13]) then
+      begin
+      Result:=Result+Copy(S,J,I-J);
+      Case P^ of
+        '\' : Result:=Result+'\\';
+        '/' : Result:=Result+'\/';
+        '"' : Result:=Result+'\"';
+        #8  : Result:=Result+'\b';
+        #9  : Result:=Result+'\t';
+        #10 : Result:=Result+'\n';
+        #12 : Result:=Result+'\f';
+        #13 : Result:=Result+'\r';
+      end;
+      J:=I+1;
+      end;
+    Inc(I);
+    Inc(P);
+    end;
+  Result:=Result+Copy(S,J,I-1);
+end;
+
+Procedure TJSWriter.WriteValue(V: TJSValue);
+
+Var
+  S : String;
+begin
+  Case V.ValueType of
+     jstUNDEFINED : S:='undefined';
+     jstNull : s:='null';
+     jstBoolean : if V.AsBoolean then s:='true' else s:='false';
+     jstString : S:='"'+EscapeString(V.AsString)+'"';
+     jstNumber :
+       if Frac(V.AsNumber)=0 then // this needs to be improved
+         Str(Round(V.AsNumber),S)
+       else
+         Str(V.AsNumber,S);
+     jstObject : ;
+     jstReference : ;
+     JSTCompletion : ;
+  end;
+  Write(S);
+end;
+
+Constructor TJSWriter.Create(AWriter: TTextWriter);
+begin
+  FWriter:=AWriter;
+  FIndentChar:=' ';
+  FOptions:=[woUseUTF8];
+end;
+
+Constructor TJSWriter.Create(Const AFileName: String);
+begin
+  Create(TFileWriter.Create(AFileName));
+  FFreeWriter:=True;
+end;
+
+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;
+
+begin
+  C:=(woCompact in Options);
+  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
+    FSkipBrackets:=True;
+    WriteJS(FD.Body);
+    If not (FD.Body.A is TJSStatementList) then
+      if C then
+        Write('; ')
+      else
+        Writeln(';');
+    end;
+  if C then
+    Write('}')
+  else
+    begin
+    undent;
+    Writeln('}');
+    end;
+end;
+
+Procedure TJSWriter.WriteEmptyBlockStatement(El: TJSEmptyBlockStatement);
+begin
+  if woCompact in Options then
+    Write('{}')
+  else
+    begin
+    Writeln('{');
+    Write('}');
+    end;
+end;
+
+Procedure TJSWriter.WriteEmptyStatement(El: TJSEmptyStatement);
+begin
+  if woEmptyStatementAsComment in options then
+    Write('/* Empty statement */')
+end;
+
+Procedure TJSWriter.WriteRegularExpressionLiteral(El: TJSRegularExpressionLiteral);
+
+begin
+  Write('/');
+  Write(EscapeString(EL.Pattern.AsString));
+  Write('/');
+  If Assigned(EL.PatternFlags) then
+    Write(EscapeString(EL.PatternFlags.AsString));
+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);
+end;
+
+Procedure TJSWriter.WriteArrayLiteral(El : TJSArrayLiteral);
+
+
+
+Var
+  Chars : Array[Boolean] of string[2] = ('[]','()');
+
+Var
+  i,C : Integer;
+  WC : Boolean;
+  BC : String[2];
+begin
+  BC:=Chars[El is TJSArguments];
+  C:=EL.Elements.Count-1;
+  if C=-1 then
+    begin
+    if el is TJSArguments then
+      Write(bc)
+    else
+      Write(bc);
+    Exit;
+    end;
+  WC:=(woCompact in Options);
+  if WC then
+    Write(Copy(BC,1,1))
+  else
+    begin
+    Writeln(Copy(BC,1,1));
+    Indent;
+    end;
+  For I:=0 to C do
+   begin
+   WriteJS(EL.Elements[i].Expr);
+   if I<C then
+     if WC then Write(', ') else Writeln(',')
+   end;
+  if not WC then
+    begin
+    Writeln('');
+    Undent;
+    end;
+  Write(Copy(BC,2,1));
+end;
+
+
+Procedure TJSWriter.WriteObjectLiteral(El : TJSObjectLiteral);
+
+
+Var
+  i,C : Integer;
+  QE,WC : Boolean;
+  S : TJSString;
+
+begin
+  C:=EL.Elements.Count-1;
+  QE:=(woQuoteElementNames in Options);
+  if C=-1 then
+    begin
+    Write('{}');
+    Exit;
+    end;
+  WC:=(woCompact in Options);
+  if WC then
+    Write('{')
+  else
+    begin
+    Writeln('{');
+    Indent;
+    end;
+  For I:=0 to C do
+   begin
+   S:=EL.Elements[i].Name;
+   if QE then
+     S:='"'+S+'"';
+   Write(S+': ');
+   Indent;
+   WriteJS(EL.Elements[i].Expr);
+   if I<C then
+     if WC then Write(', ') else Writeln(',');
+   Undent;
+   end;
+  if not WC then
+    begin
+    Writeln('');
+    Undent;
+    end;
+  Write('}');
+end;
+
+Procedure TJSWriter.WriteMemberExpression(el : TJSMemberExpression);
+
+Var
+  I : integer;
+  A : TJSArguments;
+begin
+  if el is TJSNewMemberExpression then
+    Write('new ');
+  WriteJS(el.mexpr);
+  if el is TJSDotMemberExpression then
+    begin
+    write('.');
+    Write(TJSDotMemberExpression(el).Name);
+    end
+  else if el is TJSBracketMemberExpression then
+    begin
+    write('[');
+    WriteJS(TJSBracketMemberExpression(el).Name);
+    write(']');
+    end
+  else if (el is TJSNewMemberExpression) then
+    begin
+    if (Assigned(TJSNewMemberExpression(el).Args)) then
+      WriteArrayLiteral(TJSNewMemberExpression(el).Args)
+    else
+      Write('()');
+    end;
+end;
+
+Procedure TJSWriter.WriteCallExpression(El : TJSCallExpression);
+
+Var
+  I : integer;
+  A : TJSArguments;
+begin
+  WriteJS(El.Expr);
+  if Assigned(El.Args) then
+    WriteArrayLiteral(EL.Args)
+  else
+    Write('()');
+end;
+
+Procedure TJSWriter.WriteUnary(El : TJSUnary);
+
+Var
+  S : String;
+
+begin
+  S:=El.PreFixOperator;
+  if (S<>'') then
+    Write(S);
+  WriteJS(El.A);
+  if (S='') then
+    begin
+    S:=El.PostFixOperator;
+    if (S<>'') then
+      Write(S);
+    end;
+end;
+
+Procedure TJSWriter.WriteStatementList(El : TJSStatementList);
+
+Var
+  C : Boolean;
+  B : Boolean;
+
+begin
+  C:=(woCompact in Options);
+  B:= Not FSkipBrackets;
+  if B then
+    begin
+    Write('{');
+    if not C then writeln('');
+    end;
+  if Assigned(EL.A) then
+    begin
+    WriteJS(EL.A);
+    if Assigned(EL.B) then
+      begin
+      if C then
+        Write('; ')
+      else
+        Writeln(';');
+      FSkipBrackets:=True;
+      WriteJS(EL.B);
+      end;
+    if not C then writeln(';');
+    end;
+  if B then
+    begin
+    Write('}');
+    if not C then writeln('');
+    end;
+end;
+
+Procedure TJSWriter.WriteWithStatement(El : TJSWithStatement);
+begin
+   Write('with (');
+   WriteJS(EL.A);
+   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
+  S : AnsiString;
+  B : Boolean;
+  T : TJSToken;
+
+begin
+  Write('(');
+  WriteJS(EL.A);
+  B:=False;
+  if (el is TJSBinaryExpression) then
+    begin
+    S:=TJSBinaryExpression(El).OperatorString;
+    B:=TJSBinaryExpression(El).AllowCompact;
+    end;
+  If Not (B and (woCompact in Options)) then
+    S:=' '+S+' ';
+  Write(s);
+  WriteJS(EL.B);
+  Write(')');
+end;
+
+Procedure TJSWriter.WriteConditionalExpression(El : TJSConditionalExpression);
+
+begin
+  write('(');
+  WriteJS(EL.A);
+  write(' ? ');
+  WriteJS(EL.B);
+  write(' : ');
+  WriteJS(EL.C);
+  write(')');
+end;
+
+Procedure TJSWriter.WriteAssignStatement(El : TJSAssignStatement);
+
+Var
+  S : AnsiString;
+  T : TJSToken;
+begin
+  WriteJS(EL.LHS);
+  S:=El.OperatorString;
+  If Not (woCompact in Options) then
+      S:=' '+S+' ';
+  Write(s);
+  WriteJS(EL.Expr);
+end;
+
+Procedure TJSWriter.WriteVarDeclaration(El : TJSVarDeclaration);
+
+begin
+  Write(EL.Name);
+  if Assigned(EL.Init) then
+    begin
+    Write(' = ');
+    WriteJS(EL.Init);
+    end;
+end;
+
+Procedure TJSWriter.WriteIfStatement(El : TJSIfStatement);
+
+begin
+  Write('if (');
+  WriteJS(EL.Cond);
+  Write(') ');
+  WriteJS(El.BTrue);
+  if Assigned(El.BFalse) then
+    begin
+    Write(' else ');
+    WriteJS(El.BFalse)
+    end;
+end;
+
+Procedure TJSWriter.WriteForInStatement(El : TJSForInStatement);
+
+begin
+  Write('for (');
+  if Assigned(El.LHS) then
+    WriteJS(El.LHS);
+  Write(' in ');
+  if Assigned(El.List) then
+    WriteJS(El.List);
+  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
+    WriteJS(El.Cond);
+  Write('; ');
+  if Assigned(El.Incr) then
+    WriteJS(El.Incr);
+  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
+      WriteJS(El.Body);
+    Write(' while (');
+    If Assigned(El.Cond) then
+      WriteJS(EL.Cond);
+    Write(')');
+    end
+  else
+    begin
+    Write('while (');
+    If Assigned(El.Cond) then
+      WriteJS(EL.Cond);
+    Write(') ');
+    if Assigned(El.body) then
+      WriteJS(El.Body);
+    end;
+end;
+
+Procedure TJSWriter.WriteSwitchStatement(El : TJSSwitchStatement);
+
+Var
+  C : Boolean;
+
+  Procedure WriteCaseLabel(L : TJSString);
+
+  begin
+    Write(l);
+  end;
+
+Var
+  I : Integer;
+  EC : TJSCaseElement;
+
+begin
+  C:=(woCompact in Options);
+  Write('switch (');
+  If Assigned(El.Cond) then
+    WriteJS(EL.Cond);
+  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
+      Write('case ');
+      WriteJS(EC.Expr);
+      end;
+    If C then
+      Write(': ')
+    else
+      Writeln(':');
+    if Assigned(EC.Body) then
+      begin
+      WriteJS(EC.Body);
+      if C then
+        begin
+        if Not ((EC.Body is TJSStatementList) or (EC.Body is TJSEmptyBlockStatement)) then
+          write('; ')
+        end
+      else
+        Writeln('');
+      end;
+    end;
+  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
+  Write('return ');
+  WriteJS(EL.Expr);
+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 C then writeln('');
+  FSkipBrackets:=True;
+  Indent;
+  WriteJS(El.Block);
+  Undent;
+  If C then
+    Write('} ')
+  else
+    begin
+    Writeln('');
+    Writeln('}');
+    end;
+  If (El is TJSTryCatchFinallyStatement) or (El is TJSTryCatchStatement) then
+    begin
+    Write('catch ('+El.Ident);
+    If C then
+      Write(') {')
+    else
+      Writeln(') {');
+    Indent;
+    WriteJS(EL.BCatch);
+    Undent;
+    If C then
+      if (El is TJSTryCatchFinallyStatement) then
+        Write('} ')
+      else
+        Write('}')
+    else
+      begin
+      Writeln('');
+      Writeln('}');
+      end;
+    end;
+  If (El is TJSTryCatchFinallyStatement) or (El is TJSTryFinallyStatement) then
+    begin
+    If C then
+      Write('finally {')
+    else
+      Writeln('finally {');
+    Indent;
+    WriteJS(EL.BFinally);
+    Undent;
+    If C then
+      Write('}')
+    else
+      begin
+      Writeln('');
+      Writeln('}');
+      end;
+    end;
+end;
+
+Procedure TJSWriter.WriteFunctionBody(el : TJSFunctionBody);
+
+begin
+  if Assigned(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
+  I : Integer;
+  C : Boolean;
+
+begin
+  C:=(woCompact in Options);
+  For I:=0 to EL.Statements.Count-1 do
+    begin
+    WriteJS(EL.Statements[i].Node);
+    if C then
+      if I<EL.Statements.Count-1 then
+        Write('; ')
+      else
+        Write(';')
+    else
+      WriteLn(';')
+    end;
+end;
+
+
+Procedure TJSWriter.WriteVariableStatement(el : TJSVariableStatement);
+
+begin
+  Write('var ');
+  WriteJS(EL.A);
+end;
+
+Procedure TJSWriter.WriteJS(El: TJSElement);
+begin
+{$IFDEF DEBUGJSWRITER}
+  if (EL<>Nil) then
+    system.Writeln('WriteJS : ',EL.ClassName)
+  else
+    system.Writeln('WriteJS : El = Nil');
+{$ENDIF}
+  if (El is TJSEmptyBlockStatement ) then
+    WriteEmptyBlockStatement(TJSEmptyBlockStatement(el))
+  else if (El is TJSEmptyStatement) then
+    WriteEmptyStatement(TJSEmptyStatement(el))
+  else if (el is TJSLiteral) then
+    WriteLiteral(TJSLiteral(el))
+  else if (el is TJSPrimaryExpression) then
+    WritePrimaryExpression(TJSPrimaryExpression(el))
+  else if (el is TJSArrayLiteral) then
+    WriteArrayLiteral(TJSArrayLiteral(el))
+  else if (el is TJSObjectLiteral) then
+    WriteObjectLiteral(TJSObjectLiteral(el))
+  else if (el is TJSMemberExpression) then
+    WriteMemberExpression(TJSMemberExpression(el))
+  else if (el is TJSRegularExpressionLiteral) then
+    WriteRegularExpressionLiteral(TJSRegularExpressionLiteral(El))
+  else if (el is TJSCallExpression) then
+    WriteCallExpression(TJSCallExpression(el))
+  else if (el is TJSLabeledStatement) then // Before unary
+    WriteLabeledStatement(TJSLabeledStatement(el))
+  else if (el is TJSFunctionBody) then // Before unary
+    WriteFunctionBody(TJSFunctionBody(el))
+  else if (el is TJSVariableStatement) then // Before unary
+    WriteVariableStatement(TJSVariableStatement(el))
+  else if (el is TJSUNary) then
+    WriteUnary(TJSUnary(el))
+  else if (el is TJSVariableDeclarationList) then
+    WriteVarDeclarationList(TJSVariableDeclarationList(el)) // Must be before binary
+  else if (el is TJSStatementList) then
+    WriteStatementList(TJSStatementList(el)) // Must be before binary
+  else if (el is TJSWithStatement) then
+    WriteWithStatement(TJSWithStatement(El)) // Must be before binary
+  else if (el is TJSBinary) then
+    WriteBinary(TJSBinary(el))
+  else if (el is TJSConditionalExpression) then
+    WriteConditionalExpression(TJSConditionalExpression(el))
+  else if (el is TJSAssignStatement) then
+    WriteAssignStatement(TJSAssignStatement(el))
+  else if (el is TJSVarDeclaration) then
+    WriteVarDeclaration(TJSVarDeclaration(el))
+  else if (el is TJSIfStatement) then
+    WriteIfStatement(TJSIfStatement(el))
+  else if (el is TJSTargetStatement) then
+    WriteTargetStatement(TJSTargetStatement(el))
+  else if (el is TJSReturnStatement) then
+    WriteReturnStatement(TJSReturnStatement(el))
+  else if (el is TJSTryStatement) then
+    WriteTryStatement(TJSTryStatement(el))
+  else if (el is TJSFunctionDeclarationStatement) then
+    WriteFunctionDeclarationStatement(TJSFunctionDeclarationStatement(el))
+  else if (el is TJSSourceElements) then
+    WriteSourceElements(TJSSourceElements(el))
+  else
+    Error(SErrUnknownJSClass,[El.ClassName]);
+  FSkipBrackets:=False;
+end;
+
+{ TFileWriter }
+
+Function TFileWriter.DoWrite(Const S: AnsiString) : Integer;
+begin
+  Result:=Length(S);
+  system.Write(FFile,S);
+end;
+
+Function TFileWriter.DoWrite(Const S: UnicodeString) : Integer;
+begin
+  Result:=Length(S)*SizeOf(UnicodeChar);
+  system.Write(FFile,S);
+end;
+
+Constructor TFileWriter.Create(Const AFileNAme: String);
+begin
+  FFileName:=AFileName;
+  Assign(FFile,AFileName);
+  Rewrite(FFile);
+end;
+
+Destructor TFileWriter.Destroy;
+begin
+  Close;
+  Inherited;
+end;
+
+Procedure TFileWriter.Flush;
+begin
+  system.Flush(FFile);
+end;
+
+Procedure TFileWriter.Close;
+begin
+  system.Close(FFile);
+end;
+
+{ TTextWriter }
+
+Function TTextWriter.Write(Const S: UnicodeString) : Integer;
+begin
+  Result:=DoWrite(S);
+end;
+
+Function TTextWriter.Write(Const S: String) : integer;
+begin
+  Result:=DoWrite(S);
+end;
+
+Function TTextWriter.WriteLn(Const S: String) : Integer;
+begin
+  Result:=DoWrite(S)+DoWrite(sLineBreak);
+end;
+
+Function TTextWriter.Write(Const Fmt: String; Args: Array of const) : Integer;
+
+begin
+  Result:=DoWrite(Format(Fmt,Args));
+end;
+
+Function TTextWriter.WriteLn(Const Fmt: String; Args: Array of const) : integer;
+begin
+  Result:=WriteLn(Format(Fmt,Args));
+end;
+
+Function TTextWriter.Write(Const Args: Array of const) : Integer;
+
+Var
+  V : TVarRec;
+  S : String;
+  U : UnicodeString;
+
+
+begin
+  Result:=0;
+  For V in Args do
+    begin
+    S:='';
+    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);
+    end;
+end;
+
+Function TTextWriter.WriteLn(Const Args: Array of const) : integer;
+begin
+  Result:=Write(Args)+Writeln('');
+end;
+
+end.
+

+ 2385 - 0
packages/fcl-js/tests/tcwriter.pp

@@ -0,0 +1,2385 @@
+unit tcwriter;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, fpcunit, testutils, testregistry, jsbase, jstree, jswriter;
+
+type
+
+  { TTestJSWriter }
+
+  TTestJSWriter = class(TTestCase)
+  private
+    FElement: TJSElement;
+    FTextWriter: TBufferWriter;
+    FWriter: TJSWriter;
+  protected
+    procedure SetUp; override;
+    procedure TearDown; override;
+    Procedure WriteElement(JS : TJSElement); // Set element in Element, write. Freed on teardown
+    Procedure AssertResult(Const Msg, Result : String); // Compare result;
+    Procedure AssertResult(Const Msg : string; Result : UnicodeString); // Compare result;
+    Procedure AssertWrite(Const Msg, Result : String; AElement : TJSElement); // Call writelement, compare result;
+    Procedure AssertWrite(Const Msg : string; Result : UnicodeString; AElement : TJSElement); // Call writelement, compare result;
+    Function CreateIdent(Const AName : String) : TJSPrimaryExpressionIdent;
+    Function CreateLiteral(Const AValue : TJSString) : TJSLiteral;
+    Function CreateLiteral(Const AValue : Integer) : TJSLiteral;
+    Function CreateLiteral(Const AValue : Boolean) : TJSLiteral;
+    Property TextWriter : TBufferWriter Read FTextWriter;
+    Property Writer : TJSWriter Read FWriter;
+    Property Element : TJSElement read FElement;
+  end;
+
+  TTestTestJSWriter = Class(TTestJSWriter)
+  published
+    procedure TestEmpty;
+  end;
+
+  { TTestLiteralWriter }
+
+  TTestLiteralWriter= class(TTestJSWriter)
+  published
+    Procedure TestInteger;
+    Procedure TestBooleanTrue;
+    Procedure TestBooleanFalse;
+    Procedure TestUndefined;
+    Procedure TestNull;
+    Procedure TestString;
+    Procedure TestStringQuote;
+    Procedure TestStringBackslash;
+    Procedure TestStringslash;
+    Procedure TestStringsBack;
+    Procedure TestStringsTab;
+    Procedure TestStringsLineFeed;
+    Procedure TestStringsFormFeed;
+    Procedure TestStringsCarriageReturn;
+    Procedure TestArrayEmpty;
+    Procedure TestArrayEmptyCompact;
+    Procedure TestArrayOneElement;
+    Procedure TestArrayOneElementCompact;
+    Procedure TestArrayOneElementIndent;
+    Procedure TestArrayTwoElements;
+    Procedure TestArrayTwoElementsCompact;
+    Procedure TestArrayThreeElementsCompact;
+    Procedure TestObjectEmpty;
+    Procedure TestObjectEmptyCompact;
+    Procedure TestObjectOneElement;
+    Procedure TestObjectOneElementCompact;
+    Procedure TestObjectOneElementIndent;
+    Procedure TestObjectOneElementCompactQuoted;
+    Procedure TestObjectTwoElements;
+    Procedure TestObjectTwoElementCompact;
+    Procedure TestObjectTwoElementCompactQuoted;
+    Procedure TestObjectThreeElementsCompact;
+  end;
+
+  { TTestStatementWriter }
+
+  TTestStatementWriter = class(TTestJSWriter)
+  Public
+    Procedure TestAssignment(Const Msg : String; AClass : TJSAssignStatementClass; Result : String;ACompact : Boolean);
+    Function CreateAssignment(AClass : TJSAssignStatementClass) : TJSAssignStatement;
+  published
+    Procedure TestEmptyStatement;
+    Procedure TestEmptyStatementComment;
+    Procedure TestEmptyStatementBlock;
+    Procedure TestEmptyStatementBlockIndent;
+    Procedure TestEmptyStatementBlockCompact;
+    Procedure TestVarDeclaration;
+    Procedure TestVarDeclarationInit;
+    Procedure TestVarListDeclaration;
+    Procedure TestVarListDeclarationInit;
+    Procedure TestVarDeclarationStatement;
+    Procedure TestVarListDeclarationStatement;
+    Procedure TestVarListDeclarationStatement2Vars;
+    Procedure TestReturnStatement;
+    Procedure TestLabeledStatement;
+    Procedure TestLabeledStatementCompact;
+    Procedure TestContinueStatement;
+    Procedure TestContinueTargetStatement;
+    Procedure TestBreakStatement;
+    Procedure TestBreakTargetStatement;
+    Procedure TestAssignmentStatementSimple;
+    Procedure TestAssignmentStatementSimpleCompact;
+    Procedure TestAssignmentStatementAdd;
+    Procedure TestAssignmentStatementAddCompact;
+    Procedure TestAssignmentStatementSubtract;
+    Procedure TestAssignmentStatementSubtractCompact;
+    Procedure TestAssignmentStatementMultiply;
+    Procedure TestAssignmentStatementMultiplyCompact;
+    Procedure TestAssignmentStatementDivide;
+    Procedure TestAssignmentStatementDivideCompact;
+    Procedure TestAssignmentStatementShift;
+    Procedure TestAssignmentStatementShiftCompact;
+    Procedure TestAssignmentStatementRShift;
+    Procedure TestAssignmentStatementRShiftCompact;
+    Procedure TestAssignmentStatementURShift;
+    Procedure TestAssignmentStatementURShiftCompact;
+    Procedure TestAssignmentStatementMod;
+    Procedure TestAssignmentStatementModCompact;
+    Procedure TestAssignmentStatementBinaryOr;
+    Procedure TestAssignmentStatementBinaryOrCompact;
+    Procedure TestAssignmentStatementBinaryXOr;
+    Procedure TestAssignmentStatementBinaryXOrCompact;
+    Procedure TestAssignmentStatementBinaryAnd;
+    Procedure TestAssignmentStatementBinaryAndCompact;
+    Procedure TestForStatementEmpty;
+    Procedure TestForStatementFull;
+    Procedure TestForStatementCompact;
+    Procedure TestForInStatement;
+    Procedure TestWhileStatement;
+    Procedure TestDoWhileStatement;
+    Procedure TestSwitchStatementEmpty;
+    Procedure TestSwitchStatementEmptyCompact;
+    Procedure TestSwitchStatementOneElement;
+    Procedure TestSwitchStatementOneElementCompact;
+    Procedure TestSwitchStatementTwoElements;
+    Procedure TestSwitchStatementTwoElementsCompact;
+    Procedure TestSwitchStatementTwoElementsDefault;
+    Procedure TestSwitchStatementTwoElementsDefaultCompact;
+    Procedure TestSwitchStatementTwoElementsOneEmpty;
+    Procedure TestSwitchStatementTwoElementsOneEmptyCompact;
+    Procedure TestIfThen;
+    Procedure TestIfThenElse;
+    Procedure TestStatementListEmpty;
+    Procedure TestStatementListEmptyCompact;
+    Procedure TestStatementListOneStatement;
+    Procedure TestStatementListOneStatementCompact;
+    Procedure TestStatementListTwoStatements;
+    Procedure TestStatementListTwoStatementsCompact;
+    Procedure TestEmptyFunctionDef;
+    Procedure TestEmptyFunctionDefCompact;
+    Procedure TestFunctionDefParams;
+    Procedure TestFunctionDefParamsCompact;
+    Procedure TestFunctionDefBody1;
+    Procedure TestFunctionDefBody1Compact;
+    Procedure TestFunctionDefBody2;
+    Procedure TestFunctionDefBody2Compact;
+    Procedure TestTryCatch;
+    Procedure TestTryCatchCompact;
+    Procedure TestTryFinally;
+    Procedure TestTryFinallyCompact;
+    Procedure TestTryCatchFinally;
+    Procedure TestTryCatchFinallyCompact;
+    Procedure TestWith;
+    Procedure TestWithCompact;
+    Procedure TestSourceElements;
+    Procedure TestSourceElementsCompact;
+  end;
+
+  { TTestExpressionWriter }
+
+  TTestExpressionWriter= class(TTestJSWriter)
+  Protected
+    Procedure TestUnary(Const Msg : String; AClass : TJSUnaryClass; Result : String);
+    Procedure TestBinary(Const Msg : String; AClass : TJSBinaryClass; Result : String;ACompact : Boolean);
+  Published
+    Procedure TestIdent;
+    Procedure TestThis;
+    Procedure TestThrowStatement;
+    Procedure TestUnaryDelete;
+    Procedure TestUnaryVoid;
+    Procedure TestUnaryTypeOf;
+    Procedure TestPrefixPlusPLus;
+    Procedure TestPrefixMinusMinus;
+    Procedure TestUnaryMinus;
+    Procedure TestUnaryPlus;
+    Procedure TestUnaryInv;
+    Procedure TestUnaryNot;
+    Procedure TestPostPlusPLus;
+    Procedure TestPostMinusMinus;
+    Procedure TestBinaryLogicalOr;
+    Procedure TestBinaryLogicalOrCompact;
+    Procedure TestBinaryLogicalAnd;
+    Procedure TestBinaryLogicalAndCompact;
+    Procedure TestBinaryBitwiseOr;
+    Procedure TestBinaryBitwiseOrCompact;
+    Procedure TestBinaryBitwiseAnd;
+    Procedure TestBinaryBitwiseAndCompact;
+    Procedure TestBinaryBitwiseXOr;
+    Procedure TestBinaryBitwiseXOrCompact;
+    Procedure TestBinaryEQ;
+    Procedure TestBinaryEQCompact;
+    Procedure TestBinaryNE;
+    Procedure TestBinaryNECompact;
+    Procedure TestBinarySEQ;
+    Procedure TestBinarySEQCompact;
+    Procedure TestBinarySNE;
+    Procedure TestBinarySNECompact;
+    Procedure TestBinaryLT;
+    Procedure TestBinaryLTCompact;
+    Procedure TestBinaryGT;
+    Procedure TestBinaryGTCompact;
+    Procedure TestBinaryLE;
+    Procedure TestBinaryLECompact;
+    Procedure TestBinaryGE;
+    Procedure TestBinaryGECompact;
+    Procedure TestBinaryIN;
+    Procedure TestBinaryINCompact;
+    Procedure TestBinaryInstanceOf;
+    Procedure TestBinaryInstanceOfCompact;
+    Procedure TestBinaryLShift;
+    Procedure TestBinaryLShiftOfCompact;
+    Procedure TestBinaryRShift;
+    Procedure TestBinaryRShiftOfCompact;
+    Procedure TestBinaryURShift;
+    Procedure TestBinaryURShiftOfCompact;
+    Procedure TestBinaryPlus;
+    Procedure TestBinaryPlusCompact;
+    Procedure TestBinaryMinus;
+    Procedure TestBinaryMinusCompact;
+    Procedure TestBinaryMultiply;
+    Procedure TestBinaryMultiplyCompact;
+    Procedure TestBinaryDivide;
+    Procedure TestBinaryDivideCompact;
+    Procedure TestBinaryMod;
+    Procedure TestBinaryModCompact;
+    Procedure TestBinaryComma;
+    Procedure TestBinaryCommaCompact;
+    Procedure TestDotMember;
+    Procedure TestArgMember;
+    Procedure TestNewMember;
+    Procedure TestNewMemberCompact;
+    Procedure TestNewMemberNoArgs;
+    Procedure TestCall;
+    Procedure TestCallCompact;
+    Procedure TestCallNoArgs;
+    Procedure TestConditional;
+    Procedure TestRegularExpressionLiteral;
+    Procedure TestRegularExpressionLiteralFlags;
+  end;
+
+implementation
+
+{ TTestExpressionWriter }
+
+Procedure TTestExpressionWriter.TestUnary(Const Msg: String;
+  AClass: TJSUnaryClass; Result: String);
+Var
+  U : TJSUnary;
+
+begin
+  U:=AClass.Create(0,0);
+  U.A:=CreateIdent('a');
+  AssertWrite(Msg,Result,U);
+end;
+
+Procedure TTestExpressionWriter.TestBinary(Const Msg: String;
+  AClass: TJSBinaryClass; Result: String; ACompact: Boolean);
+Var
+  U : TJSBinary;
+
+begin
+  if ACompact then
+    Writer.Options:=Writer.Options+[woCompact];
+  U:=AClass.Create(0,0);
+  U.A:=CreateIdent('a');
+  U.B:=CreateIdent('b');
+  AssertWrite(Msg,Result,U);
+end;
+
+Procedure TTestExpressionWriter.TestIdent;
+
+begin
+  AssertWrite('ABC','ABC',CreateIdent('ABC'));
+end;
+
+Procedure TTestExpressionWriter.TestThis;
+begin
+  AssertWrite('this','this',TJSPrimaryExpressionThis.Create(0,0));
+end;
+
+Procedure TTestExpressionWriter.TestThrowStatement;
+
+begin
+  TestUnary('Throw expresssion',TJSThrowStatement,'throw a');
+end;
+
+Procedure TTestExpressionWriter.TestUnaryDelete;
+begin
+  TestUnary('Delete expresssion',TJSUnaryDeleteExpression,'delete a');
+end;
+
+Procedure TTestExpressionWriter.TestUnaryVoid;
+begin
+  TestUnary('Void expresssion',TJSUnaryVoidExpression,'void a');
+end;
+
+Procedure TTestExpressionWriter.TestUnaryTypeOf;
+begin
+  TestUnary('typeof expresssion',TJSUnaryTypeOfExpression,'typeof a');
+end;
+
+Procedure TTestExpressionWriter.TestPrefixPlusPLus;
+begin
+  TestUnary('prefix ++ expresssion',TJSUnaryPrePlusPlusExpression,'++a');
+end;
+
+Procedure TTestExpressionWriter.TestPrefixMinusMinus;
+begin
+  TestUnary('prefix -- expresssion',TJSUnaryPreMinusMinusExpression,'--a');
+end;
+
+Procedure TTestExpressionWriter.TestUnaryMinus;
+begin
+  TestUnary('unary - expresssion',TJSUnaryMinusExpression,'-a');
+end;
+
+Procedure TTestExpressionWriter.TestUnaryPlus;
+begin
+  TestUnary('unary + expresssion',TJSUnaryPlusExpression,'+a');
+end;
+
+Procedure TTestExpressionWriter.TestUnaryInv;
+begin
+  TestUnary('unary invert expresssion',TJSUnaryInvExpression,'~a');
+end;
+
+Procedure TTestExpressionWriter.TestUnaryNot;
+begin
+  TestUnary('unary not expresssion',TJSUnaryNotExpression,'!a');
+end;
+
+Procedure TTestExpressionWriter.TestPostPlusPLus;
+begin
+  TestUnary('postfix ++ expresssion',TJSUnaryPostPlusPlusExpression,'a++');
+end;
+
+Procedure TTestExpressionWriter.TestPostMinusMinus;
+begin
+  TestUnary('postfix -- expresssion',TJSUnaryPostMinusMinusExpression,'a--');
+end;
+
+Procedure TTestExpressionWriter.TestBinaryLogicalOr;
+begin
+  TestBinary('logical or',TJSLogicalOrExpression,'(a || b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryLogicalOrCompact;
+begin
+  TestBinary('logical or',TJSLogicalOrExpression,'(a||b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryLogicalAnd;
+begin
+  TestBinary('logical or',TJSLogicalAndExpression,'(a && b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryLogicalAndCompact;
+begin
+  TestBinary('logical or',TJSLogicalAndExpression,'(a&&b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryBitwiseOr;
+begin
+  TestBinary('Bitwise or',TJSBitwiseOrExpression,'(a | b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryBitwiseOrCompact;
+begin
+  TestBinary('Bitwise or',TJSBitwiseOrExpression,'(a|b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryBitwiseAnd;
+begin
+  TestBinary('Bitwise and',TJSBitwiseAndExpression,'(a & b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryBitwiseAndCompact;
+begin
+  TestBinary('Bitwise and',TJSBitwiseAndExpression,'(a&b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryBitwiseXOr;
+begin
+  TestBinary('Bitwise xor',TJSBitwiseXOrExpression,'(a ^ b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryBitwiseXOrCompact;
+begin
+  TestBinary('Bitwise xor',TJSBitwiseXOrExpression,'(a^b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryEQ;
+begin
+  TestBinary('Equal',TJSEqualityExpressionEQ,'(a == b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryEQCompact;
+begin
+  TestBinary('Equal',TJSEqualityExpressionEQ,'(a==b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryNE;
+begin
+  TestBinary('Not Equal',TJSEqualityExpressionNE,'(a != b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryNECompact;
+begin
+  TestBinary('Not Equal',TJSEqualityExpressionNE,'(a!=b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinarySEQ;
+begin
+  TestBinary('Strictly Equal',TJSEqualityExpressionSEQ,'(a === b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinarySEQCompact;
+begin
+  TestBinary('Strictly Equal',TJSEqualityExpressionSEQ,'(a===b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinarySNE;
+begin
+  TestBinary('Strictly Equal',TJSEqualityExpressionSNE,'(a !== b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinarySNECompact;
+begin
+  TestBinary('Strictly Equal',TJSEqualityExpressionSNE,'(a!==b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryLT;
+begin
+  TestBinary('Less than',TJSRelationalExpressionLT,'(a < b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryLTCompact;
+begin
+  TestBinary('Less than',TJSRelationalExpressionLT,'(a<b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryGT;
+begin
+  TestBinary('Greater than',TJSRelationalExpressionGT,'(a > b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryGTCompact;
+begin
+  TestBinary('Greater than',TJSRelationalExpressionGT,'(a>b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryLE;
+begin
+  TestBinary('Less than or equal',TJSRelationalExpressionLE,'(a <= b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryLECompact;
+begin
+  TestBinary('Less than or equal',TJSRelationalExpressionLE,'(a<=b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryGE;
+begin
+  TestBinary('Greater than or equal',TJSRelationalExpressionGE,'(a >= b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryGECompact;
+begin
+  TestBinary('Greater than or equal',TJSRelationalExpressionGE,'(a>=b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryIN;
+begin
+  TestBinary('Prop in Object',TJSRelationalExpressionIN,'(a in b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryINCompact;
+begin
+  TestBinary('Prop in Object',TJSRelationalExpressionIN,'(a in b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryInstanceOf;
+begin
+  TestBinary('A instanceof Object',TJSRelationalExpressionInStanceOf,'(a instanceof b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryInstanceOfCompact;
+begin
+  TestBinary('A instanceof Object',TJSRelationalExpressionInStanceOf,'(a instanceof b)',true);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryLShift;
+begin
+  TestBinary('A lshift B',TJSLShiftExpression,'(a << b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryLShiftOfCompact;
+begin
+  TestBinary('A lshift B',TJSLShiftExpression,'(a<<b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryRShift;
+begin
+  TestBinary('A rshift B',TJSRShiftExpression,'(a >> b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryRShiftOfCompact;
+begin
+  TestBinary('A rshift B',TJSRShiftExpression,'(a>>b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryURShift;
+begin
+  TestBinary('A urshift B',TJSURShiftExpression,'(a >>> b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryURShiftOfCompact;
+begin
+  TestBinary('A urshift B',TJSURShiftExpression,'(a>>>b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryPlus;
+begin
+  TestBinary('A plus B',TJSAdditiveExpressionPlus,'(a + b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryPlusCompact;
+begin
+  TestBinary('A plus B',TJSAdditiveExpressionPlus,'(a+b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryMinus;
+begin
+  TestBinary('A minus B',TJSAdditiveExpressionMinus,'(a - b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryMinusCompact;
+begin
+  TestBinary('A minus B',TJSAdditiveExpressionMinus,'(a-b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryMultiply;
+begin
+  TestBinary('A multiply B',TJSMultiplicativeExpressionMul,'(a * b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryMultiplyCompact;
+begin
+  TestBinary('A multiply B',TJSMultiplicativeExpressionMul,'(a*b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryDivide;
+begin
+  TestBinary('A divide B',TJSMultiplicativeExpressionDiv,'(a / b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryDivideCompact;
+begin
+  TestBinary('A divide B',TJSMultiplicativeExpressionDiv,'(a/b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryMod;
+begin
+  TestBinary('A mod B',TJSMultiplicativeExpressionMod,'(a % b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryModCompact;
+begin
+  TestBinary('A mod B',TJSMultiplicativeExpressionMod,'(a%b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryComma;
+begin
+  TestBinary('A comma B',TJSCommaExpression,'(a , b)',False);
+end;
+
+Procedure TTestExpressionWriter.TestBinaryCommaCompact;
+begin
+  TestBinary('A comma B',TJSCommaExpression,'(a,b)',True);
+end;
+
+Procedure TTestExpressionWriter.TestDotMember;
+Var
+  U : TJSDotMemberExpression;
+
+begin
+  U:=TJSDotMemberExpression.Create(0,0);
+  U.Mexpr:=CreateIdent('a');
+  U.Name:='b';
+  AssertWrite('member b of object a (a.b)','a.b',U);
+end;
+
+Procedure TTestExpressionWriter.TestArgMember;
+Var
+  U : TJSBracketMemberExpression;
+
+begin
+  U:=TJSBracketMemberExpression.Create(0,0);
+  U.Mexpr:=CreateIdent('a');
+  U.Name:=CreateIdent('b');
+  AssertWrite('member b of object a (a[b])','a[b]',U);
+end;
+
+Procedure TTestExpressionWriter.TestNewMember;
+Var
+  U : TJSNewMemberExpression;
+
+begin
+  U:=TJSNewMemberExpression.Create(0,0);
+  U.Mexpr:=CreateIdent('a');;
+  U.Args:=TJSArguments.Create(0,0);
+  U.Args.Elements.AddElement;
+  U.Args.Elements[0].Expr:=CreateLiteral(123);
+  AssertWrite('member b of object a (a[b])','new a('+slinebreak+'123'+sLineBreak+')',U);
+end;
+
+Procedure TTestExpressionWriter.TestNewMemberCompact;
+
+Var
+  U : TJSNewMemberExpression;
+
+begin
+  Writer.Options:=Writer.Options+[woCompact];
+  U:=TJSNewMemberExpression.Create(0,0);
+  U.Mexpr:=CreateIdent('a');
+  U.Args:=TJSArguments.Create(0,0);
+  U.Args.Elements.AddElement;
+  U.Args.Elements[0].Expr:=CreateLiteral(123);
+  AssertWrite('new a(123)','new a(123)',U);
+end;
+
+Procedure TTestExpressionWriter.TestNewMemberNoArgs;
+Var
+  U : TJSNewMemberExpression;
+
+begin
+  U:=TJSNewMemberExpression.Create(0,0);
+  U.Mexpr:=CreateIdent('a');
+  AssertWrite('new a()','new a()',U);
+end;
+
+Procedure TTestExpressionWriter.TestCall;
+Var
+  U : TJSCallExpression;
+
+begin
+  U:=TJSCallExpression.Create(0,0);
+  U.Expr:=CreateIdent('a');
+  U.Args:=TJSArguments.Create(0,0);
+  U.Args.Elements.AddElement;
+  U.Args.Elements[0].Expr:=CreateLiteral(123);
+  AssertWrite('call a(123)','a('+slinebreak+'123'+sLineBreak+')',U);
+end;
+
+Procedure TTestExpressionWriter.TestCallCompact;
+Var
+  U : TJSCallExpression;
+
+begin
+  Writer.Options:=Writer.Options+[woCompact];
+  U:=TJSCallExpression.Create(0,0);
+  U.Expr:=CreateIdent('a');
+  U.Args:=TJSArguments.Create(0,0);
+  U.Args.Elements.AddElement;
+  U.Args.Elements[0].Expr:=CreateLiteral(123);
+  AssertWrite('call a(123)','a(123)',U);
+end;
+
+Procedure TTestExpressionWriter.TestCallNoArgs;
+Var
+  U : TJSCallExpression;
+
+begin
+  U:=TJSCallExpression.Create(0,0);
+  U.Expr:=CreateIdent('a');
+  AssertWrite('call a()','a()',U);
+end;
+
+Procedure TTestExpressionWriter.TestConditional;
+Var
+  U : TJSConditionalExpression;
+
+begin
+  U:=TJSConditionalExpression.Create(0,0);
+  U.A:=CreateIdent('a');
+  U.B:=CreateIdent('b');
+  U.C:=CreateIdent('c');
+  AssertWrite('a ? b : c','(a ? b : c)',U);
+end;
+
+Procedure TTestExpressionWriter.TestRegularExpressionLiteral;
+
+Var
+  S : TJSRegularExpressionLiteral;
+begin
+  S:=TJSRegularExpressionLiteral.Create(0,0);
+  S.Pattern.AsString:='a';
+  AssertWrite('/a/','/a/',S);
+end;
+
+Procedure TTestExpressionWriter.TestRegularExpressionLiteralFlags;
+Var
+  S : TJSRegularExpressionLiteral;
+begin
+  S:=TJSRegularExpressionLiteral.Create(0,0);
+  S.Pattern.AsString:='a';
+  S.PatternFlags.AsString:='g';
+  AssertWrite('/a/g','/a/g',S);
+end;
+
+{ ---------------------------------------------------------------------
+  TTestStatementWriter
+  ---------------------------------------------------------------------}
+
+Procedure TTestStatementWriter.TestAssignment(Const Msg: String;
+  AClass: TJSAssignStatementClass; Result: String; ACompact: Boolean);
+Var
+  U : TJSAssignStatement;
+begin
+  if ACompact then
+    Writer.Options:=Writer.Options+[woCompact];
+  U:=CreateAssignment(AClass);
+  AssertWrite(Msg,Result,U);
+end;
+
+Function TTestStatementWriter.CreateAssignment(AClass: TJSAssignStatementClass
+  ): TJSAssignStatement;
+begin
+  if AClass=Nil then
+     AClass := TJSSimpleAssignStatement;
+  Result:=AClass.Create(0,0);
+  Result.LHS:=CreateIdent('a');
+  Result.Expr:=CreateIdent('b');
+end;
+
+Procedure TTestStatementWriter.TestEmptyStatement;
+
+begin
+  AssertWrite('Empty statement','',TJSEmptyStatement.Create(0,0));
+end;
+
+Procedure TTestStatementWriter.TestEmptyStatementComment;
+begin
+  Writer.Options:=[woEmptyStatementAsComment,woUseUTF8];
+  AssertWrite('Empty statement as comment','/* Empty statement */',TJSEmptyStatement.Create(0,0));
+end;
+
+Procedure TTestStatementWriter.TestEmptyStatementBlock;
+begin
+  AssertWrite('Empty statement block','{'+sLineBreak+'}',TJSEmptyBlockStatement.Create(0,0));
+end;
+
+Procedure TTestStatementWriter.TestEmptyStatementBlockIndent;
+begin
+  Writer.IndentSize:=2;
+  Writer.Indent;
+  AssertWrite('Empty statement block','  {'+sLineBreak+'  }',TJSEmptyBlockStatement.Create(0,0));
+end;
+
+Procedure TTestStatementWriter.TestEmptyStatementBlockCompact;
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  AssertWrite('Empty statement block','{}',TJSEmptyBlockStatement.Create(0,0));
+end;
+
+Procedure TTestStatementWriter.TestVarDeclaration;
+
+Var
+  V : TJSVarDeclaration;
+  L : TJSPrimaryExpressionIdent;
+
+begin
+  V:=TJSVarDeclaration.Create(0,0);
+  V.Name:='a';
+  AssertWrite('simple var','a',V);
+end;
+
+Procedure TTestStatementWriter.TestVarDeclarationInit;
+Var
+  V : TJSVarDeclaration;
+  L : TJSLiteral;
+
+begin
+  V:=TJSVarDeclaration.Create(0,0);
+  V.Name:='a';
+  V.Init:=CreateLiteral(1);
+  AssertWrite('simple var, init ','a = 1',V);
+end;
+
+Procedure TTestStatementWriter.TestVarListDeclaration;
+Var
+  B,L : TJSVariableDeclarationList;
+  V : TJSVarDeclaration;
+
+begin
+  L:=TJSVariableDeclarationList.Create(0,0);
+  V:=TJSVarDeclaration.Create(0,0);
+  V.Name:='a';
+  L.A:=V;
+  B:=TJSVariableDeclarationList.Create(0,0);
+  V:=TJSVarDeclaration.Create(0,0);
+  V.Name:='b';
+  B.A:=V;
+  V.Init:=CreateLiteral(1);
+  L.B:=B;
+  AssertWrite('simple var list ','a, b = 1',L);
+end;
+
+Procedure TTestStatementWriter.TestVarListDeclarationInit;
+Var
+  B,L : TJSVariableDeclarationList;
+  V : TJSVarDeclaration;
+
+
+begin
+  L:=TJSVariableDeclarationList.Create(0,0);
+  V:=TJSVarDeclaration.Create(0,0);;
+  V.Name:='a';
+  L.A:=V;
+  B:=TJSVariableDeclarationList.Create(0,0);
+  V:=TJSVarDeclaration.Create(0,0);;
+  V.Name:='b';
+  B.A:=V;
+  L.B:=B;
+  AssertWrite('simple var list ','a, b',L);
+end;
+
+Procedure TTestStatementWriter.TestVarDeclarationStatement;
+
+Var
+  S : TJSVariableStatement;
+  V : TJSVarDeclaration;
+  L : TJSPrimaryExpressionIdent;
+
+begin
+  S:=TJSVariableStatement.Create(0,0);
+  V:=TJSVarDeclaration.Create(0,0);
+  S.A:=V;
+  V.Name:='a';
+  AssertWrite('simple var','var a',S);
+end;
+
+Procedure TTestStatementWriter.TestVarListDeclarationStatement;
+
+Var
+  S : TJSVariableStatement;
+  V : TJSVarDeclaration;
+  L : TJSVariableDeclarationList;
+
+begin
+  S:=TJSVariableStatement.Create(0,0);
+  L:=TJSVariableDeclarationList.Create(0,0);
+  V:=TJSVarDeclaration.Create(0,0);
+  L.A:=V;
+  S.A:=L;
+  V.Name:='a';
+  AssertWrite('simple var','var a',S);
+end;
+
+Procedure TTestStatementWriter.TestVarListDeclarationStatement2Vars;
+Var
+  S : TJSVariableStatement;
+  V : TJSVarDeclaration;
+  L : TJSVariableDeclarationList;
+
+begin
+  S:=TJSVariableStatement.Create(0,0);
+  L:=TJSVariableDeclarationList.Create(0,0);
+  S.A:=L;
+  V:=TJSVarDeclaration.Create(0,0);
+  L.A:=V;
+  V.Name:='a';
+  L.B:=TJSVariableDeclarationList.Create(0,0);
+  L:=TJSVariableDeclarationList(L.B);
+  V:=TJSVarDeclaration.Create(0,0);
+  L.A:=V;
+  V.Name:='b';
+  AssertWrite('simple 2 vars','var a, b',S);
+end;
+
+Procedure TTestStatementWriter.TestReturnStatement;
+Var
+  S : TJSReturnStatement;
+
+begin
+  S:=TJSReturnStatement.Create(0,0);
+  S.Expr:=CreateIdent('a');
+  AssertWrite('simple return','return a',S);
+end;
+
+Procedure TTestStatementWriter.TestLabeledStatement;
+Var
+  LS : TJSLabeledStatement;
+  S : TJSReturnStatement;
+
+begin
+  LS:=TJSLabeledStatement.Create(0,0);
+  LS.TheLabel:=TJSLabel.Create;
+  LS.TheLabel.Name:='loc';
+  S:=TJSReturnStatement.Create(0,0);
+  S.Expr:=CreateIDent('a');
+  LS.A:=S;
+  AssertWrite('simple return','loc:'+sLineBreak+'return a',LS);
+end;
+
+Procedure TTestStatementWriter.TestLabeledStatementCompact;
+Var
+  LS : TJSLabeledStatement;
+  S : TJSReturnStatement;
+
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  LS:=TJSLabeledStatement.Create(0,0);
+  LS.TheLabel:=TJSLabel.Create;
+  LS.TheLabel.Name:='loc';
+  S:=TJSReturnStatement.Create(0,0);
+  S.Expr:=CreateIdent('a');
+  LS.A:=S;
+  AssertWrite('simple return','loc: return a',LS);
+end;
+
+Procedure TTestStatementWriter.TestContinueStatement;
+
+Var
+  S : TJSContinueStatement;
+
+begin
+  S:=TJSContinueStatement.Create(0,0);
+  AssertWrite('simple continue','continue',S);
+end;
+
+Procedure TTestStatementWriter.TestContinueTargetStatement;
+
+Var
+  S : TJSContinueStatement;
+
+begin
+  S:=TJSContinueStatement.Create(0,0);
+  S.TargetName:='a';
+  AssertWrite('continue a','continue a',S);
+end;
+
+Procedure TTestStatementWriter.TestBreakStatement;
+
+Var
+  S : TJSBreakStatement;
+
+begin
+  S:=TJSBreakStatement.Create(0,0);
+  AssertWrite('simple break','break',S);
+end;
+
+Procedure TTestStatementWriter.TestBreakTargetStatement;
+Var
+  S : TJSBreakStatement;
+
+begin
+  S:=TJSBreakStatement.Create(0,0);
+  S.TargetName:='a';
+  AssertWrite('simple break a','break a',S);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementSimple;
+begin
+  TestAssignment('Simple assignment',TJSSimpleAssignStatement,'a = b',False);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementSimpleCompact;
+begin
+  TestAssignment('Simple assignment',TJSSimpleAssignStatement,'a=b',True);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementAdd;
+begin
+  TestAssignment('Add assignment',TJSAddEqAssignStatement,'a += b',False);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementAddCompact;
+begin
+  TestAssignment('Add assignment',TJSAddEqAssignStatement,'a+=b',True);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementSubtract;
+begin
+  TestAssignment('Subtract assignment',TJSSubEqAssignStatement,'a -= b',False);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementSubtractCompact;
+begin
+  TestAssignment('Subtract assignment',TJSSubEqAssignStatement,'a-=b',True);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementMultiply;
+begin
+  TestAssignment('Multiply assignment',TJSMulEqAssignStatement,'a *= b',False);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementMultiplyCompact;
+begin
+  TestAssignment('Multiply assignment',TJSMulEqAssignStatement,'a*=b',True);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementDivide;
+begin
+  TestAssignment('Divide assignment',TJSDivEqAssignStatement,'a /= b',False);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementDivideCompact;
+begin
+  TestAssignment('Divide assignment',TJSDivEqAssignStatement,'a/=b',True);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementShift;
+begin
+  TestAssignment('Shift assignment',TJSLShiftEqAssignStatement,'a <<= b',False);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementShiftCompact;
+begin
+  TestAssignment('Shift assignment',TJSLShiftEqAssignStatement,'a<<=b',True);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementRShift;
+begin
+  TestAssignment('RShift assignment',TJSRShiftEqAssignStatement,'a >>= b',False);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementRShiftCompact;
+begin
+  TestAssignment('RShift assignment',TJSRShiftEqAssignStatement,'a>>=b',True);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementURShift;
+begin
+  TestAssignment('URShift assignment',TJSURShiftEqAssignStatement,'a >>>= b',False);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementURShiftCompact;
+begin
+  TestAssignment('URShift assignment',TJSURShiftEqAssignStatement,'a>>>=b',True);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementMod;
+begin
+  TestAssignment('Mod assignment',TJSModEqAssignStatement,'a %= b',False);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementModCompact;
+begin
+  TestAssignment('Mod assignment',TJSModEqAssignStatement,'a%=b',True);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementBinaryOr;
+begin
+  TestAssignment('Binary or assignment',TJSOrEqAssignStatement,'a |= b',False);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementBinaryOrCompact;
+begin
+  TestAssignment('Binary or assignment',TJSOrEqAssignStatement,'a |= b',False);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementBinaryXOr;
+begin
+  TestAssignment('Binary xor assignment',TJSXOrEqAssignStatement,'a ^= b',False);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementBinaryXOrCompact;
+begin
+  TestAssignment('Binary xor assignment',TJSXOrEqAssignStatement,'a^=b',True);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementBinaryAnd;
+begin
+  TestAssignment('Binary and assignment',TJSAndEqAssignStatement,'a &= b',False);
+end;
+
+Procedure TTestStatementWriter.TestAssignmentStatementBinaryAndCompact;
+begin
+  TestAssignment('Binary and assignment',TJSAndEqAssignStatement,'a&=b',True);
+end;
+
+Procedure TTestStatementWriter.TestForStatementEmpty;
+
+Var
+  S : TJSForStatement;
+begin
+  S:=TJSForStatement.Create(0,0);
+  S.Body:=TJSEmptyBlockStatement.Create(0,0);
+  AssertWrite('neverending for','for (; ; ) {'+sLineBreak+'}',S);
+end;
+
+Procedure TTestStatementWriter.TestForStatementFull;
+
+
+Var
+  S : TJSForStatement;
+  UPP : TJSUnaryPostPlusPlusExpression;
+  CL : TJSRelationalExpressionLT;
+  L : TJSLiteral;
+  sa : TJSSimpleAssignStatement;
+
+begin
+  SA:=TJSSimpleAssignStatement.Create(0,0);
+  SA.LHS:=CreateIdent('i');
+  SA.Expr:=CreateLiteral(0);
+  UPP:=TJSUnaryPostPlusPlusExpression.Create(0,0);
+  UPP.A:=CreateIdent('i');
+  CL:=TJSRelationalExpressionLT.Create(0,0);
+  CL.A:=CreateIdent('i');
+  CL.B:=CreateLiteral(10);
+  S:=TJSForStatement.Create(0,0);
+  S.Init:=SA;
+  S.Incr:=UPP;
+  S.Cond:=CL;
+  S.Body:=TJSEmptyBlockStatement.Create(0,0);
+  AssertWrite('for i:=0 to 9','for (i = 0; (i < 10); i++) {'+sLineBreak+'}',S);
+end;
+
+Procedure TTestStatementWriter.TestForStatementCompact;
+Var
+  S : TJSForStatement;
+  UPP : TJSUnaryPostPlusPlusExpression;
+  CL : TJSRelationalExpressionLT;
+  L : TJSLiteral;
+  sa : TJSSimpleAssignStatement;
+
+begin
+  SA:=TJSSimpleAssignStatement.Create(0,0);
+  SA.LHS:=CreateIdent('i');
+  SA.Expr:=CreateLiteral(0);
+  UPP:=TJSUnaryPostPlusPlusExpression.Create(0,0);
+  UPP.A:=CreateIdent('i');
+  CL:=TJSRelationalExpressionLT.Create(0,0);
+  CL.A:=CreateIdent('i');
+  CL.B:=CreateLiteral(10);
+  S:=TJSForStatement.Create(0,0);
+  S.Init:=SA;
+  S.Incr:=UPP;
+  S.Cond:=CL;
+  S.Body:=TJSEmptyBlockStatement.Create(0,0);
+  Writer.Options:=[woCompact,woUseUTF8];
+  AssertWrite('for i:=0 to 9','for (i=0; (i<10); i++) {}',S);
+end;
+
+Procedure TTestStatementWriter.TestForInStatement;
+
+Var
+  S : TJSForInStatement;
+
+begin
+  S:=TJSForInStatement.Create(0,0);
+  S.LHS:=CreateIdent('a');
+  S.List:=CreateIdent('b');
+  S.Body:=TJSEmptyBlockStatement.Create(0,0);
+  AssertWrite('for a in b','for (a in b) {'+sLineBreak+'}',S);
+end;
+
+Procedure TTestStatementWriter.TestWhileStatement;
+Var
+  S : TJSWhileStatement;
+
+begin
+  S:=TJSWhileStatement.Create(0,0);
+  S.Cond:=CreateIdent('a');
+  S.Body:=TJSEmptyBlockStatement.Create(0,0);
+  AssertWrite('while a ','while (a) {'+sLineBreak+'}',S);
+end;
+
+Procedure TTestStatementWriter.TestDoWhileStatement;
+
+Var
+  S : TJSDoWhileStatement;
+
+begin
+  S:=TJSDoWhileStatement.Create(0,0);
+  S.Cond:=CreateIdent('a');
+  S.Body:=TJSEmptyBlockStatement.Create(0,0);
+  AssertWrite('do while a ','do {'+sLineBreak+'} while (a)',S);
+end;
+
+Procedure TTestStatementWriter.TestSwitchStatementEmpty;
+Var
+  S : TJSSwitchStatement;
+
+begin
+  S:=TJSSwitchStatement.Create(0,0);
+  S.Cond:=CreateIdent('a');
+  AssertWrite('switch ','switch (a) {'+sLineBreak+'}',S);
+end;
+
+Procedure TTestStatementWriter.TestSwitchStatementEmptyCompact;
+
+Var
+  S : TJSSwitchStatement;
+
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  S:=TJSSwitchStatement.Create(0,0);
+  S.Cond:=CreateIdent('a');
+  AssertWrite('switch ','switch (a) {}',S);
+end;
+
+Procedure TTestStatementWriter.TestSwitchStatementOneElement;
+
+Var
+  S : TJSSwitchStatement;
+  C : TJSCaseElement;
+begin
+//  Writer.Options:=[woCompact,woUseUTF8];
+  S:=TJSSwitchStatement.Create(0,0);
+  S.Cond:=CreateIdent('a');
+  C:=S.Cases.AddCase;
+  C.Body:=TJSEmptyBlockStatement.Create(0,0);;
+  C.Expr:=CreateIdent('c');
+  AssertWrite('switch ','switch (a) {'+sLineBreak+'case c:'+sLineBreak+'{'+sLineBreak+'}'+sLineBreak+'}',S);
+end;
+
+Procedure TTestStatementWriter.TestSwitchStatementOneElementCompact;
+Var
+  S : TJSSwitchStatement;
+  C : TJSCaseElement;
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  S:=TJSSwitchStatement.Create(0,0);
+  S.Cond:=CreateIdent('a');
+  C:=S.Cases.AddCase;
+  C.Body:=TJSEmptyBlockStatement.Create(0,0);;
+  C.Expr:=CreateIdent('c');
+  AssertWrite('switch ','switch (a) {case c: {}}',S);
+end;
+
+Procedure TTestStatementWriter.TestSwitchStatementTwoElements;
+Var
+  S : TJSSwitchStatement;
+  C : TJSCaseElement;
+begin
+//  Writer.Options:=[woCompact,woUseUTF8];
+  S:=TJSSwitchStatement.Create(0,0);
+  S.Cond:=CreateIdent('a');
+  C:=S.Cases.AddCase;
+  C.Body:=TJSEmptyBlockStatement.Create(0,0);;
+  C.Expr:=CreateIdent('c');
+  C:=S.Cases.AddCase;
+  C.Body:=TJSEmptyBlockStatement.Create(0,0);;
+  C.Expr:=CreateIdent('d');
+  AssertWrite('switch ','switch (a) {'+sLineBreak+'case c:'+sLineBreak+'{'+sLineBreak+'}'+sLineBreak+'case d:'+sLineBreak+'{'+sLineBreak+'}'+sLineBreak+'}',S);
+end;
+
+Procedure TTestStatementWriter.TestSwitchStatementTwoElementsCompact;
+
+Var
+  S : TJSSwitchStatement;
+  C : TJSCaseElement;
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  S:=TJSSwitchStatement.Create(0,0);
+  S.Cond:=CreateIdent('a');
+  C:=S.Cases.AddCase;
+  C.Body:=TJSEmptyBlockStatement.Create(0,0);;
+  C.Expr:=CreateIdent('c');
+  C:=S.Cases.AddCase;
+  C.Body:=TJSEmptyBlockStatement.Create(0,0);;
+  C.Expr:=CreateIdent('d');
+  AssertWrite('switch ','switch (a) {case c: {}case d: {}}',S);
+end;
+
+Procedure TTestStatementWriter.TestSwitchStatementTwoElementsDefault;
+Var
+  S : TJSSwitchStatement;
+  C : TJSCaseElement;
+begin
+  S:=TJSSwitchStatement.Create(0,0);
+  S.Cond:=CreateIdent('a');
+  C:=S.Cases.AddCase;
+  C.Body:=TJSEmptyBlockStatement.Create(0,0);;
+  C.Expr:=CreateIdent('c');
+  C:=S.Cases.AddCase;
+  C.Body:=TJSEmptyBlockStatement.Create(0,0);;
+  C.Expr:=CreateIdent('d');
+  C:=S.Cases.AddCase;
+  C.Body:=TJSEmptyBlockStatement.Create(0,0);;
+  S.TheDefault:=C;
+  AssertWrite('switch ','switch (a) {'+sLineBreak+'case c:'+sLineBreak+'{'+sLineBreak+'}'+sLineBreak+'case d:'+sLineBreak+'{'+sLineBreak+'}'+sLineBreak+'default:'+sLineBreak+'{'+sLineBreak+'}'+sLineBreak+'}',S);
+end;
+
+Procedure TTestStatementWriter.TestSwitchStatementTwoElementsDefaultCompact;
+Var
+  S : TJSSwitchStatement;
+  C : TJSCaseElement;
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  S:=TJSSwitchStatement.Create(0,0);
+  S.Cond:=CreateIdent('a');
+  C:=S.Cases.AddCase;
+  C.Body:=TJSEmptyBlockStatement.Create(0,0);;
+  C.Expr:=CreateIdent('c');
+  C:=S.Cases.AddCase;
+  C.Body:=TJSEmptyBlockStatement.Create(0,0);;
+  C.Expr:=CreateIdent('d');
+  C:=S.Cases.AddCase;
+  C.Body:=TJSEmptyBlockStatement.Create(0,0);;
+  S.TheDefault:=C;
+  AssertWrite('switch ','switch (a) {case c: {}case d: {}default: {}}',S);
+end;
+
+Procedure TTestStatementWriter.TestSwitchStatementTwoElementsOneEmpty;
+Var
+  S : TJSSwitchStatement;
+  C : TJSCaseElement;
+begin
+  S:=TJSSwitchStatement.Create(0,0);
+  S.Cond:=CreateIdent('a');
+  C:=S.Cases.AddCase;
+  C.Expr:=CreateIdent('c');
+  C:=S.Cases.AddCase;
+  C.Body:=TJSEmptyBlockStatement.Create(0,0);;
+  C.Expr:=CreateIdent('d');
+  C:=S.Cases.AddCase;
+  C.Body:=TJSEmptyBlockStatement.Create(0,0);;
+  S.TheDefault:=C;
+  AssertWrite('switch ','switch (a) {'+sLineBreak+'case c:'+sLineBreak+'case d:'+sLineBreak+'{'+sLineBreak+'}'+sLineBreak+'default:'+sLineBreak+'{'+sLineBreak+'}'+sLineBreak+'}',S);
+end;
+
+Procedure TTestStatementWriter.TestSwitchStatementTwoElementsOneEmptyCompact;
+Var
+  S : TJSSwitchStatement;
+  C : TJSCaseElement;
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  S:=TJSSwitchStatement.Create(0,0);
+  S.Cond:=CreateIdent('a');
+  C:=S.Cases.AddCase;
+  C.Expr:=CreateIdent('c');
+  C:=S.Cases.AddCase;
+  C.Body:=TJSEmptyBlockStatement.Create(0,0);;
+  C.Expr:=CreateIdent('d');
+  C:=S.Cases.AddCase;
+  C.Body:=TJSEmptyBlockStatement.Create(0,0);;
+  S.TheDefault:=C;
+  AssertWrite('switch ','switch (a) {case c: case d: {}default: {}}',S);
+end;
+
+Procedure TTestStatementWriter.TestIfThen;
+Var
+  S : TJSIfStatement;
+
+begin
+//  Writer.Options:=[woCompact,woUseUTF8];
+  S:=TJSIfStatement.Create(0,0);
+  S.Cond:=CreateIdent('a');
+  S.btrue:=TJSEmptyBlockStatement.Create(0,0);
+  AssertWrite('if then','if (a) {'+sLineBreak+'}',S);
+end;
+
+Procedure TTestStatementWriter.TestIfThenElse;
+Var
+  S : TJSIfStatement;
+
+begin
+//  Writer.Options:=[woCompact,woUseUTF8];
+  S:=TJSIfStatement.Create(0,0);
+  S.Cond:=CreateIdent('a');
+  S.btrue:=TJSEmptyBlockStatement.Create(0,0);
+  S.bfalse:=TJSEmptyBlockStatement.Create(0,0);
+  AssertWrite('if then','if (a) {'+sLineBreak+'} else {'+sLineBreak+'}',S);
+end;
+
+Procedure TTestStatementWriter.TestStatementListEmpty;
+Var
+  S : TJSStatementList;
+
+begin
+//  Writer.Options:=[woCompact,woUseUTF8];
+  S:=TJSStatementList.Create(0,0);
+  AssertWrite('Statement list','{'+sLineBreak+'}'+sLineBreak,S);
+end;
+
+Procedure TTestStatementWriter.TestStatementListEmptyCompact;
+Var
+  S : TJSStatementList;
+
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  S:=TJSStatementList.Create(0,0);
+  AssertWrite('Statement list','{}',S);
+end;
+
+Procedure TTestStatementWriter.TestStatementListOneStatement;
+Var
+  S : TJSStatementList;
+
+begin
+//  Writer.Options:=[woCompact,woUseUTF8];
+  S:=TJSStatementList.Create(0,0);
+  S.A:=CreateAssignment(nil);
+  AssertWrite('Statement list','{'+sLineBreak+'a = b;'+sLineBreak+'}'+sLineBreak,S);
+end;
+
+Procedure TTestStatementWriter.TestStatementListOneStatementCompact;
+
+Var
+  S : TJSStatementList;
+
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  S:=TJSStatementList.Create(0,0);
+  S.A:=CreateAssignment(nil);
+  AssertWrite('Statement list','{a=b}',S);
+end;
+
+Procedure TTestStatementWriter.TestStatementListTwoStatements;
+Var
+  S : TJSStatementList;
+
+begin
+//  Writer.Options:=[woCompact,woUseUTF8];
+  S:=TJSStatementList.Create(0,0);
+  S.A:=CreateAssignment(nil);
+  S.B:=CreateAssignment(nil);
+  AssertWrite('Statement list','{'+sLineBreak+'a = b;'+sLineBreak+'a = b;'+sLineBreak+'}'+sLineBreak,S);
+end;
+
+Procedure TTestStatementWriter.TestStatementListTwoStatementsCompact;
+Var
+  S : TJSStatementList;
+
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  S:=TJSStatementList.Create(0,0);
+  S.A:=CreateAssignment(nil);
+  S.B:=CreateAssignment(nil);
+  AssertWrite('Statement list','{a=b; a=b}',S);
+end;
+
+Procedure TTestStatementWriter.TestEmptyFunctionDef;
+
+Var
+  FD : TJSFunctionDeclarationStatement;
+
+begin
+  FD:=TJSFunctionDeclarationStatement.Create(0,0);
+  FD.AFunction:=TJSFuncDef.Create;
+  FD.AFunction.Name:='a';
+  AssertWrite('Empty function','function a() {'+sLineBreak+'}'+sLineBreak,FD);
+end;
+
+Procedure TTestStatementWriter.TestEmptyFunctionDefCompact;
+
+Var
+  FD : TJSFunctionDeclarationStatement;
+
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  FD:=TJSFunctionDeclarationStatement.Create(0,0);
+  FD.AFunction:=TJSFuncDef.Create;
+  FD.AFunction.Name:='a';
+  AssertWrite('Empty function, compact','function a() {}',FD);
+end;
+
+Procedure TTestStatementWriter.TestFunctionDefParams;
+Var
+  FD : TJSFunctionDeclarationStatement;
+
+begin
+//  Writer.Options:=[woCompact,woUseUTF8];
+  FD:=TJSFunctionDeclarationStatement.Create(0,0);
+  FD.AFunction:=TJSFuncDef.Create;
+  FD.AFunction.Name:='a';
+  FD.AFunction.Params.Add('b');
+  FD.AFunction.Params.Add('c');
+  FD.AFunction.Params.Add('d');
+
+  AssertWrite('Empty function, 3 params','function a(b, c, d) {'+sLineBreak+'}'+sLineBreak,FD);
+end;
+
+Procedure TTestStatementWriter.TestFunctionDefParamsCompact;
+
+Var
+  FD : TJSFunctionDeclarationStatement;
+
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  FD:=TJSFunctionDeclarationStatement.Create(0,0);
+  FD.AFunction:=TJSFuncDef.Create;
+  FD.AFunction.Name:='a';
+  FD.AFunction.Params.Add('b');
+  FD.AFunction.Params.Add('c');
+  FD.AFunction.Params.Add('d');
+  AssertWrite('Empty function, 3 params, compact','function a(b,c,d) {}',FD);
+end;
+
+Procedure TTestStatementWriter.TestFunctionDefBody1;
+
+Var
+  FD : TJSFunctionDeclarationStatement;
+  R : TJSReturnStatement;
+
+begin
+  Writer.IndentSize:=2;
+  // Writer.Options:=[woCompact,woUseUTF8];
+  FD:=TJSFunctionDeclarationStatement.Create(0,0);
+  FD.AFunction:=TJSFuncDef.Create;
+  FD.AFunction.Name:='a';
+  FD.AFunction.Body:=TJSFunctionBody.Create(0,0);
+  R:=TJSReturnStatement.Create(0,0);
+  R.Expr:=CreateLiteral(0);
+  FD.AFunction.Body.A:=R;
+  AssertWrite('1 statement, ','function a() {'+sLineBreak+'  return 0;'+sLineBreak+'}'+sLineBreak,FD);
+end;
+
+Procedure TTestStatementWriter.TestFunctionDefBody1Compact;
+Var
+  FD : TJSFunctionDeclarationStatement;
+  R : TJSReturnStatement;
+
+begin
+  Writer.IndentSize:=2;
+  Writer.Options:=[woCompact,woUseUTF8];
+  FD:=TJSFunctionDeclarationStatement.Create(0,0);
+  FD.AFunction:=TJSFuncDef.Create;
+  FD.AFunction.Name:='a';
+  FD.AFunction.Body:=TJSFunctionBody.Create(0,0);
+  R:=TJSReturnStatement.Create(0,0);
+  R.Expr:=CreateLiteral(0);
+  FD.AFunction.Body.A:=R;
+  AssertWrite('1 statement, compact','function a() {return 0; }',FD);
+end;
+
+Procedure TTestStatementWriter.TestFunctionDefBody2;
+Var
+  FD : TJSFunctionDeclarationStatement;
+  R : TJSReturnStatement;
+  L : TJSStatementList;
+  A : TJSAssignStatement;
+  M : TJSMultiplicativeExpressionMul;
+
+begin
+  Writer.IndentSize:=2;
+//  Writer.Options:=[woCompact,woUseUTF8];
+  FD:=TJSFunctionDeclarationStatement.Create(0,0);
+  FD.AFunction:=TJSFuncDef.Create;
+  FD.AFunction.Name:='a';
+  FD.AFunction.Body:=TJSFunctionBody.Create(0,0);
+  FD.AFunction.Params.Add('b');
+  R:=TJSReturnStatement.Create(0,0);
+  R.Expr:=CreateIdent('b');
+  L:=TJSStatementList.Create(0,0);
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  M:=TJSMultiplicativeExpressionMul.Create(0,0);
+  M.A:=CreateIdent('b');
+  M.B:=CreateLiteral(10);
+  A.Expr:=M;
+  L.A:=A;
+  L.B:=R;
+  FD.AFunction.Body.A:=L;
+  AssertWrite('Function, 2 statements','function a(b) {'+sLineBreak+'  b = (b * 10);'+sLineBreak+'  return b;'+sLineBreak+'}'+sLineBreak,FD);
+end;
+
+Procedure TTestStatementWriter.TestFunctionDefBody2Compact;
+Var
+  FD : TJSFunctionDeclarationStatement;
+  R : TJSReturnStatement;
+  L : TJSStatementList;
+  A : TJSAssignStatement;
+  M : TJSMultiplicativeExpressionMul;
+
+begin
+  Writer.IndentSize:=2;
+  Writer.Options:=[woCompact,woUseUTF8];
+  FD:=TJSFunctionDeclarationStatement.Create(0,0);
+  FD.AFunction:=TJSFuncDef.Create;
+  FD.AFunction.Name:='a';
+  FD.AFunction.Body:=TJSFunctionBody.Create(0,0);
+  FD.AFunction.Params.Add('b');
+  R:=TJSReturnStatement.Create(0,0);
+  R.Expr:=CreateIdent('b');
+  L:=TJSStatementList.Create(0,0);
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  M:=TJSMultiplicativeExpressionMul.Create(0,0);
+  M.A:=CreateIdent('b');
+  M.B:=CreateLiteral(10);
+  A.Expr:=M;
+  L.A:=A;
+  L.B:=R;
+  FD.AFunction.Body.A:=L;
+  AssertWrite('Function, 2 statements, compact','function a(b) {b=(b*10); return b}',FD);
+end;
+
+Procedure TTestStatementWriter.TestTryCatch;
+
+Var
+  T : TJSTryCatchStatement;
+  A : TJSAssignStatement;
+  M : TJSMultiplicativeExpressionMul;
+
+begin
+  Writer.IndentSize:=2;
+  T:=TJSTryCatchStatement.Create(0,0);
+  T.Ident:='e';
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  M:=TJSMultiplicativeExpressionMul.Create(0,0);
+  M.A:=CreateIdent('b');
+  M.B:=CreateLiteral(10);
+  A.Expr:=M;
+  T.Block:=A;
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  A.Expr:=CreateLiteral(1);
+  T.BCatch:=A;
+  AssertWrite('Try catch','try {'+sLineBreak+'  b = (b * 10)'+sLineBreak+'}'+sLineBreak+'catch (e) {'+sLineBreak+'  b = 1'+sLineBreak+'}'+sLineBreak,T);
+end;
+
+Procedure TTestStatementWriter.TestTryCatchCompact;
+Var
+  T : TJSTryCatchStatement;
+  A : TJSAssignStatement;
+  M : TJSMultiplicativeExpressionMul;
+
+begin
+  Writer.IndentSize:=2;
+  Writer.Options:=[woCompact,woUseUTF8];
+  T:=TJSTryCatchStatement.Create(0,0);
+  T.Ident:='e';
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  M:=TJSMultiplicativeExpressionMul.Create(0,0);
+  M.A:=CreateIdent('b');
+  M.B:=CreateLiteral(10);
+  A.Expr:=M;
+  T.Block:=A;
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  A.Expr:=CreateLiteral(1);
+  T.BCatch:=A;
+  AssertWrite('Try catch compact','try {b=(b*10)} catch (e) {b=1}',T);
+end;
+
+Procedure TTestStatementWriter.TestTryFinally;
+
+Var
+  T : TJSTryFinallyStatement;
+  A : TJSAssignStatement;
+  M : TJSMultiplicativeExpressionMul;
+
+begin
+  Writer.IndentSize:=2;
+  T:=TJSTryFinallyStatement.Create(0,0);
+  T.Ident:='e';
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  M:=TJSMultiplicativeExpressionMul.Create(0,0);
+  M.A:=CreateIdent('b');
+  M.B:=CreateLiteral(10);
+  A.Expr:=M;
+  T.Block:=A;
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  A.Expr:=CreateLiteral(1);
+  T.BFinally:=A;
+  AssertWrite('Try finally ','try {'+sLineBreak+'  b = (b * 10)'+sLineBreak+'}'+sLineBreak+'finally {'+sLineBreak+'  b = 1'+sLineBreak+'}'+sLineBreak,T);
+end;
+
+Procedure TTestStatementWriter.TestTryFinallyCompact;
+
+Var
+  T : TJSTryFinallyStatement;
+  A : TJSAssignStatement;
+  M : TJSMultiplicativeExpressionMul;
+
+begin
+  Writer.IndentSize:=2;
+  Writer.Options:=[woCompact,woUseUTF8];
+  T:=TJSTryFinallyStatement.Create(0,0);
+  T.Ident:='e';
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  M:=TJSMultiplicativeExpressionMul.Create(0,0);
+  M.A:=CreateIdent('b');
+  M.B:=CreateLiteral(10);
+  A.Expr:=M;
+  T.Block:=A;
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  A.Expr:=CreateLiteral(1);
+  T.BFinally:=A;
+  AssertWrite('Try finally compact','try {b=(b*10)} finally {b=1}',T);
+end;
+
+Procedure TTestStatementWriter.TestTryCatchFinally;
+Var
+  T : TJSTryCatchFinallyStatement;
+  A : TJSAssignStatement;
+  M : TJSMultiplicativeExpressionMul;
+
+begin
+  Writer.IndentSize:=2;
+  T:=TJSTryCatchFinallyStatement.Create(0,0);
+  T.Ident:='e';
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  M:=TJSMultiplicativeExpressionMul.Create(0,0);
+  M.A:=CreateIdent('b');
+  M.B:=CreateLiteral(10);
+  A.Expr:=M;
+  T.Block:=A;
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  A.Expr:=CreateLiteral(10);
+  T.BCatch:=A;
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  A.Expr:=CreateLiteral(1);
+  T.BFinally:=A;
+  AssertWrite('Try finally ','try {'+sLineBreak+'  b = (b * 10)'+sLineBreak+'}'+sLineBreak+'catch (e) {'+sLineBreak+'  b = 10'+sLineBreak+'}'+sLineBreak+'finally {'+sLineBreak+'  b = 1'+sLineBreak+'}'+sLineBreak,T);
+end;
+
+Procedure TTestStatementWriter.TestTryCatchFinallyCompact;
+Var
+  T : TJSTryCatchFinallyStatement;
+  A : TJSAssignStatement;
+  M : TJSMultiplicativeExpressionMul;
+
+begin
+  Writer.IndentSize:=2;
+  Writer.Options:=[woCompact,woUseUTF8];
+  T:=TJSTryCatchFinallyStatement.Create(0,0);
+  T.Ident:='e';
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  M:=TJSMultiplicativeExpressionMul.Create(0,0);
+  M.A:=CreateIdent('b');
+  M.B:=CreateLiteral(10);
+  A.Expr:=M;
+  T.Block:=A;
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  A.Expr:=CreateLiteral(10);
+  T.BCatch:=A;
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  A.Expr:=CreateLiteral(1);
+  T.BFinally:=A;
+  AssertWrite('Try finally ','try {b=(b*10)} catch (e) {b=10} finally {b=1}',T);
+end;
+
+Procedure TTestStatementWriter.TestWith;
+Var
+  T : TJSWithStatement;
+  A : TJSAssignStatement;
+  M : TJSMultiplicativeExpressionMul;
+
+begin
+  Writer.IndentSize:=2;
+//  Writer.Options:=[woCompact,woUseUTF8];
+  T:=TJSWithStatement.Create(0,0);
+  T.A:=CreateIdent('e');
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  M:=TJSMultiplicativeExpressionMul.Create(0,0);
+  M.A:=CreateIdent('b');
+  M.B:=CreateLiteral(10);
+  A.Expr:=M;
+  T.B:=A;
+  AssertWrite('With statement ','with (e)'+slineBreak+'  b = (b * 10)',T);
+end;
+
+Procedure TTestStatementWriter.TestWithCompact;
+Var
+  T : TJSWithStatement;
+  A : TJSAssignStatement;
+  M : TJSMultiplicativeExpressionMul;
+
+begin
+  Writer.IndentSize:=2;
+  Writer.Options:=[woCompact,woUseUTF8];
+  T:=TJSWithStatement.Create(0,0);
+  T.A:=CreateIdent('e');
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  M:=TJSMultiplicativeExpressionMul.Create(0,0);
+  M.A:=CreateIdent('b');
+  M.B:=CreateLiteral(10);
+  A.Expr:=M;
+  T.B:=A;
+  AssertWrite('With statement ','with (e) b=(b*10)',T);
+end;
+
+Procedure TTestStatementWriter.TestSourceElements;
+Var
+  T : TJSSourceElements;
+  A : TJSAssignStatement;
+  M : TJSMultiplicativeExpressionMul;
+
+begin
+  Writer.IndentSize:=2;
+  //  Writer.Options:=[woCompact,woUseUTF8];
+  T:=TJSSourceElements.Create(0,0);
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  M:=TJSMultiplicativeExpressionMul.Create(0,0);
+  M.A:=CreateIdent('b');
+  M.B:=CreateLiteral(10);
+  A.Expr:=M;
+  T.Statements.AddNode.Node:=A;
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('c');
+  M:=TJSMultiplicativeExpressionMul.Create(0,0);
+  M.A:=CreateIdent('c');
+  M.B:=CreateLiteral(2);
+  A.Expr:=M;
+  T.Statements.AddNode.Node:=A;
+  AssertWrite('Statement lists ','b = (b * 10);'+sLineBreak+'c = (c * 2);'+sLineBreak,T);
+end;
+
+Procedure TTestStatementWriter.TestSourceElementsCompact;
+Var
+  T : TJSSourceElements;
+  A : TJSAssignStatement;
+  M : TJSMultiplicativeExpressionMul;
+
+begin
+  Writer.IndentSize:=2;
+  Writer.Options:=[woCompact,woUseUTF8];
+  T:=TJSSourceElements.Create(0,0);
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('b');
+  M:=TJSMultiplicativeExpressionMul.Create(0,0);
+  M.A:=CreateIdent('b');
+  M.B:=CreateLiteral(10);
+  A.Expr:=M;
+  T.Statements.AddNode.Node:=A;
+  A:=TJSSimpleAssignStatement.Create(0,0);
+  A.LHS:=CreateIdent('c');
+  M:=TJSMultiplicativeExpressionMul.Create(0,0);
+  M.A:=CreateIdent('c');
+  M.B:=CreateLiteral(2);
+  A.Expr:=M;
+  T.Statements.AddNode.Node:=A;
+  AssertWrite('Statement lists compact','b=(b*10); c=(c*2);',T);
+end;
+
+{ ---------------------------------------------------------------------
+  TTestLiteralWriter
+  ---------------------------------------------------------------------}
+
+Procedure TTestLiteralWriter.TestInteger;
+
+begin
+  AssertWrite('1','1',CreateLiteral(1));
+end;
+
+Procedure TTestLiteralWriter.TestBooleanTrue;
+
+Var
+  L : TJSLiteral;
+begin
+  L:=TJSLiteral.Create(0,0,'');
+  L.Value.Asboolean:=True;
+  AssertWrite('true','true',L);
+end;
+
+Procedure TTestLiteralWriter.TestBooleanFalse;
+Var
+  L : TJSLiteral;
+begin
+  L:=TJSLiteral.Create(0,0,'');
+  L.Value.Asboolean:=False;
+  AssertWrite('false','false',L);
+end;
+
+Procedure TTestLiteralWriter.TestUndefined;
+Var
+  L : TJSLiteral;
+begin
+  L:=TJSLiteral.Create(0,0,'');
+  AssertWrite('undefined','undefined',L);
+end;
+
+Procedure TTestLiteralWriter.TestNull;
+
+Var
+  L : TJSLiteral;
+begin
+  L:=TJSLiteral.Create(0,0,'');
+  L.Value.IsNull:=True;
+  AssertWrite('null','null',L);
+end;
+
+Procedure TTestLiteralWriter.TestString;
+Var
+  L : TJSLiteral;
+begin
+  L:=TJSLiteral.Create(0,0,'');
+  L.Value.AsString:='abcd';
+  AssertWrite('abcd','"abcd"',L);
+end;
+
+Procedure TTestLiteralWriter.TestStringQuote;
+Var
+  L : TJSLiteral;
+begin
+  L:=TJSLiteral.Create(0,0,'');
+  L.Value.AsString:='ab"cd';
+  AssertWrite('ab"cd','"ab\"cd"',L);
+end;
+
+Procedure TTestLiteralWriter.TestStringBackslash;
+Var
+  L : TJSLiteral;
+begin
+  L:=TJSLiteral.Create(0,0,'');
+  L.Value.AsString:='ab\cd';
+  AssertWrite('ab\cd','"ab\\cd"',L);
+end;
+
+Procedure TTestLiteralWriter.TestStringslash;
+Var
+  L : TJSLiteral;
+begin
+  L:=TJSLiteral.Create(0,0,'');
+  L.Value.AsString:='ab/cd';
+  AssertWrite('ab/cd','"ab\/cd"',L);
+end;
+
+Procedure TTestLiteralWriter.TestStringsBack;
+Var
+  L : TJSLiteral;
+begin
+  L:=TJSLiteral.Create(0,0,'');
+  L.Value.AsString:='ab'#8'cd';
+  AssertWrite('ab'#8'cd','"ab\bcd"',L);
+end;
+
+Procedure TTestLiteralWriter.TestStringsTab;
+Var
+  L : TJSLiteral;
+begin
+  L:=TJSLiteral.Create(0,0,'');
+  L.Value.AsString:='ab'#9'cd';
+  AssertWrite('ab'#9'cd','"ab\tcd"',L);
+end;
+
+Procedure TTestLiteralWriter.TestStringsLineFeed;
+Var
+  L : TJSLiteral;
+begin
+  L:=TJSLiteral.Create(0,0,'');
+  L.Value.AsString:='ab'#10'cd';
+  AssertWrite('ab'#10'cd','"ab\ncd"',L);
+end;
+
+Procedure TTestLiteralWriter.TestStringsFormFeed;
+Var
+  L : TJSLiteral;
+begin
+  L:=TJSLiteral.Create(0,0,'');
+  L.Value.AsString:='ab'#12'cd';
+  AssertWrite('ab'#12'cd','"ab\fcd"',L);
+end;
+
+Procedure TTestLiteralWriter.TestStringsCarriageReturn;
+Var
+  L : TJSLiteral;
+begin
+  L:=TJSLiteral.Create(0,0,'');
+  L.Value.AsString:='ab'#13'cd';
+  AssertWrite('ab'#13'cd','"ab\rcd"',L);
+end;
+
+Procedure TTestLiteralWriter.TestArrayEmpty;
+
+Var
+  L : TJSArrayLiteral;
+
+begin
+  L:=TJSArrayLiteral.Create(0,0);
+  AssertWrite('Empty array ','[]',L); // Always
+end;
+
+Procedure TTestLiteralWriter.TestArrayEmptyCompact;
+Var
+  L : TJSArrayLiteral;
+
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  L:=TJSArrayLiteral.Create(0,0);
+  AssertWrite('Empty array ','[]',L);
+end;
+
+Procedure TTestLiteralWriter.TestArrayOneElement;
+Var
+  L : TJSArrayLiteral;
+  I : TJSLiteral;
+
+begin
+  L:=TJSArrayLiteral.Create(0,0);
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=1;
+  L.Elements.AddElement.Expr:=I;
+  AssertWrite('Empty array ','['+sLineBreak+'1'+sLineBreak+']',L);
+end;
+
+Procedure TTestLiteralWriter.TestArrayOneElementCompact;
+
+Var
+  L : TJSArrayLiteral;
+  I : TJSLiteral;
+
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  L:=TJSArrayLiteral.Create(0,0);
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=1;
+  L.Elements.AddElement.Expr:=I;
+  AssertWrite('Empty array ','[1]',L);
+end;
+
+Procedure TTestLiteralWriter.TestArrayOneElementIndent;
+Var
+  L : TJSArrayLiteral;
+  I : TJSLiteral;
+
+begin
+  Writer.IndentSize:=2;
+  L:=TJSArrayLiteral.Create(0,0);
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=1;
+  L.Elements.AddElement.Expr:=I;
+  AssertWrite('Empty array ','['+sLineBreak+'  1'+sLineBreak+']',L);
+end;
+
+Procedure TTestLiteralWriter.TestArrayTwoElements;
+
+Var
+  L : TJSArrayLiteral;
+  I : TJSLiteral;
+
+begin
+  L:=TJSArrayLiteral.Create(0,0);
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=1;
+  L.Elements.AddElement.Expr:=I;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=2;
+  L.Elements.AddElement.Expr:=I;
+  AssertWrite('Empty array ','['+sLineBreak+'1,'+sLineBreak+'2'+sLineBreak+']',L);
+end;
+
+Procedure TTestLiteralWriter.TestArrayTwoElementsCompact;
+Var
+  L : TJSArrayLiteral;
+  I : TJSLiteral;
+
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  L:=TJSArrayLiteral.Create(0,0);
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=1;
+  L.Elements.AddElement.Expr:=I;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=2;
+  L.Elements.AddElement.Expr:=I;
+  AssertWrite('Empty array ','[1, 2]',L);
+end;
+
+Procedure TTestLiteralWriter.TestArrayThreeElementsCompact;
+Var
+  L : TJSArrayLiteral;
+  I : TJSLiteral;
+
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  L:=TJSArrayLiteral.Create(0,0);
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=1;
+  L.Elements.AddElement.Expr:=I;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=2;
+  L.Elements.AddElement.Expr:=I;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=3;
+  L.Elements.AddElement.Expr:=I;
+  AssertWrite('Empty array ','[1, 2, 3]',L);
+end;
+
+Procedure TTestLiteralWriter.TestObjectEmpty;
+
+Var
+  L : TJSObjectLiteral;
+
+begin
+  L:=TJSObjectLiteral.Create(0,0);
+  AssertWrite('Empty object ','{}',L); // Always
+end;
+
+Procedure TTestLiteralWriter.TestObjectEmptyCompact;
+Var
+  L : TJSObjectLiteral;
+
+begin
+  Writer.Options:=[woCompact,woUseUTF8];
+  L:=TJSObjectLiteral.Create(0,0);
+  AssertWrite('Empty object ','{}',L); // Always
+end;
+
+Procedure TTestLiteralWriter.TestObjectOneElement;
+
+Var
+  L : TJSObjectLiteral;
+  E : TJSObjectLiteralElement;
+  I : TJSLiteral;
+
+begin
+  L:=TJSObjectLiteral.Create(0,0);
+  E:=L.Elements.AddElement;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=1;
+  E.Expr:=I;
+  E.Name:='abc';
+  AssertWrite('Empty object ','{'+slineBreak+'abc: 1'+sLineBreak+'}',L);
+end;
+
+Procedure TTestLiteralWriter.TestObjectOneElementCompact;
+
+Var
+  L : TJSObjectLiteral;
+  E : TJSObjectLiteralElement;
+  I : TJSLiteral;
+
+begin
+  L:=TJSObjectLiteral.Create(0,0);
+  E:=L.Elements.AddElement;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=1;
+  E.Expr:=I;
+  E.Name:='abc';
+  Writer.Options:=[woCompact,woUseUTF8];
+  AssertWrite('Empty object ','{abc: 1}',L);
+end;
+
+Procedure TTestLiteralWriter.TestObjectOneElementIndent;
+
+Var
+  L : TJSObjectLiteral;
+  E : TJSObjectLiteralElement;
+  I : TJSLiteral;
+begin
+  L:=TJSObjectLiteral.Create(0,0);
+  E:=L.Elements.AddElement;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=1;
+  E.Expr:=I;
+  E.Name:='abc';
+  Writer.IndentSize:=2;
+  AssertWrite('Empty object ','{'+slineBreak+'  abc: 1'+sLineBreak+'}',L);
+end;
+
+Procedure TTestLiteralWriter.TestObjectOneElementCompactQuoted;
+Var
+  L : TJSObjectLiteral;
+  E : TJSObjectLiteralElement;
+  I : TJSLiteral;
+
+begin
+  L:=TJSObjectLiteral.Create(0,0);
+  E:=L.Elements.AddElement;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=1;
+  E.Expr:=I;
+  E.Name:='abc';
+  Writer.Options:=[woCompact,woUseUTF8,woQuoteElementNames];
+  AssertWrite('Empty object ','{"abc": 1}',L);
+end;
+
+Procedure TTestLiteralWriter.TestObjectTwoElements;
+Var
+  L : TJSObjectLiteral;
+  E : TJSObjectLiteralElement;
+  I : TJSLiteral;
+
+begin
+  L:=TJSObjectLiteral.Create(0,0);
+  E:=L.Elements.AddElement;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=1;
+  E.Expr:=I;
+  E.Name:='abc';
+  E:=L.Elements.AddElement;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=2;
+  E.Expr:=I;
+  E.Name:='efg';
+  AssertWrite('Empty object ','{'+slineBreak+'abc: 1,'+sLineBreak+'efg: 2'+slineBreak+'}',L);
+end;
+
+Procedure TTestLiteralWriter.TestObjectTwoElementCompact;
+
+Var
+  L : TJSObjectLiteral;
+  E : TJSObjectLiteralElement;
+  I : TJSLiteral;
+
+begin
+  L:=TJSObjectLiteral.Create(0,0);
+  E:=L.Elements.AddElement;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=1;
+  E.Expr:=I;
+  E.Name:='abc';
+  E:=L.Elements.AddElement;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=2;
+  E.Expr:=I;
+  E.Name:='efg';
+  Writer.Options:=[woCompact,woUseUTF8];
+  AssertWrite('Empty object ','{abc: 1, efg: 2}',L);
+end;
+
+Procedure TTestLiteralWriter.TestObjectTwoElementCompactQuoted;
+Var
+  L : TJSObjectLiteral;
+  E : TJSObjectLiteralElement;
+  I : TJSLiteral;
+
+begin
+  L:=TJSObjectLiteral.Create(0,0);
+  E:=L.Elements.AddElement;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=1;
+  E.Expr:=I;
+  E.Name:='abc';
+  E:=L.Elements.AddElement;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=2;
+  E.Expr:=I;
+  E.Name:='efg';
+  Writer.Options:=[woCompact,woUseUTF8,woQuoteElementNames];
+  AssertWrite('Empty object ','{"abc": 1, "efg": 2}',L);
+end;
+
+Procedure TTestLiteralWriter.TestObjectThreeElementsCompact;
+Var
+  L : TJSObjectLiteral;
+  E : TJSObjectLiteralElement;
+  I : TJSLiteral;
+
+begin
+  L:=TJSObjectLiteral.Create(0,0);
+  E:=L.Elements.AddElement;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=1;
+  E.Expr:=I;
+  E.Name:='abc';
+  E:=L.Elements.AddElement;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=2;
+  E.Expr:=I;
+  E.Name:='efg';
+  E:=L.Elements.AddElement;
+  I:=TJSLiteral.Create(0,0);
+  I.Value.AsNumber:=3;
+  E.Expr:=I;
+  E.Name:='hij';
+  Writer.Options:=[woCompact,woUseUTF8];
+  AssertWrite('Empty object ','{abc: 1, efg: 2, hij: 3}',L);
+end;
+
+{ ---------------------------------------------------------------------
+  TTestJSWriter
+  ---------------------------------------------------------------------}
+
+procedure TTestJSWriter.SetUp;
+begin
+  FTextWriter:=TBufferWriter.Create(120);
+  FWriter:=TJSWriter.Create(FTextWriter);
+end;
+
+procedure TTestJSWriter.TearDown;
+begin
+  FreeAndNil(FWriter);
+  FreeAndNil(FTextWriter);
+  FreeAndNil(FElement);
+end;
+
+Procedure TTestJSWriter.WriteElement(JS: TJSElement);
+begin
+  FElement:=JS;
+  FWriter.WriteJS(JS);
+end;
+
+Procedure TTestJSWriter.AssertResult(Const Msg, Result: String);
+
+Var
+  S : AnsiString;
+begin
+  S:=FTextWriter.AsAnsistring;
+  AssertEquals(Msg,Result,S);
+end;
+
+Procedure TTestJSWriter.AssertResult(Const Msg: string; Result: UnicodeString);
+
+Var
+  S : UnicodeString;
+begin
+  S:=FTextWriter.AsUnicodeString;
+  AssertEquals(Msg,Result,S);
+end;
+
+Procedure TTestJSWriter.AssertWrite(Const Msg, Result: String;
+  AElement: TJSElement);
+begin
+  WriteElement(AElement);
+  AssertResult(Msg,Result);
+end;
+
+Procedure TTestJSWriter.AssertWrite(Const Msg: string; Result: UnicodeString;
+  AElement: TJSElement);
+begin
+  WriteElement(AElement);
+  AssertResult(Msg,Result);
+end;
+
+Function TTestJSWriter.CreateIdent(Const AName: String): TJSPrimaryExpressionIdent;
+begin
+  Result:=TJSPrimaryExpressionIdent.Create(0,0);
+  Result.Name:=AName;
+end;
+
+Function TTestJSWriter.CreateLiteral(Const AValue: TJSString): TJSLiteral;
+begin
+  Result:=TJSLiteral.Create(0,0);
+  Result.Value.AsString:=Avalue;
+end;
+
+Function TTestJSWriter.CreateLiteral(Const AValue: Integer): TJSLiteral;
+begin
+  Result:=TJSLiteral.Create(0,0);
+  Result.Value.AsNumber:=Avalue;
+end;
+
+Function TTestJSWriter.CreateLiteral(Const AValue: Boolean): TJSLiteral;
+begin
+  Result:=TJSLiteral.Create(0,0);
+  Result.Value.AsBoolean:=Avalue;
+end;
+
+{ ---------------------------------------------------------------------
+  TTestTestJSWriter
+  ---------------------------------------------------------------------}
+
+procedure TTestTestJSWriter.TestEmpty;
+begin
+  AssertNotNull('Have text writer',TextWriter);
+  AssertNotNull('Have JS writer',Writer);
+  AssertNull('Have no element',Element);
+  AssertSame('Correct text writer for js writer',TextWriter,Writer.Writer);
+  AssertEquals('No indent',0,Writer.IndentSize);
+  if not (Writer.Options=[woUseUTF8]) then
+    Fail('Options are not using UTF8');
+end;
+
+
+Initialization
+
+  RegisterTests([TTestTestJSWriter,TTestLiteralWriter,TTestExpressionWriter,TTestStatementWriter]);
+end.
+

+ 239 - 99
packages/fcl-js/tests/testjs.lpi

@@ -1,19 +1,21 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <CONFIG>
   <ProjectOptions>
-    <Version Value="7"/>
+    <Version Value="9"/>
     <General>
       <MainUnit Value="0"/>
-      <TargetFileExt Value=""/>
-      <Icon Value="0"/>
       <UseXPManifest Value="True"/>
-      <ActiveEditorIndexAtStart Value="1"/>
+      <Icon Value="0"/>
+      <ActiveWindowIndexAtStart Value="0"/>
     </General>
     <VersionInfo>
-      <ProjectVersion Value=""/>
       <Language Value=""/>
       <CharSet Value=""/>
+      <StringTable ProductVersion=""/>
     </VersionInfo>
+    <BuildModes Count="1">
+      <Item1 Name="default" Default="True"/>
+    </BuildModes>
     <PublishOptions>
       <Version Value="2"/>
       <IgnoreBinaries Value="False"/>
@@ -23,228 +25,365 @@
     <RunParams>
       <local>
         <FormatVersion Value="1"/>
-        <CommandLineParams Value="-a --format=plain"/>
+        <CommandLineParams Value="--suite=TTestStatementWriter"/>
         <LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
       </local>
     </RunParams>
-    <RequiredPackages Count="2">
+    <RequiredPackages Count="1">
       <Item1>
-        <PackageName Value="FPCUnitConsoleRunner"/>
-      </Item1>
-      <Item2>
         <PackageName Value="FCL"/>
-      </Item2>
+      </Item1>
     </RequiredPackages>
-    <Units Count="7">
+    <Units Count="16">
       <Unit0>
         <Filename Value="testjs.lpr"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="testjs"/>
-        <CursorPos X="11" Y="7"/>
+        <WindowIndex Value="1"/>
         <TopLine Value="1"/>
-        <EditorIndex Value="0"/>
+        <CursorPos X="48" Y="3"/>
         <UsageCount Value="201"/>
-        <Loaded Value="True"/>
+        <LoadedDesigner Value="True"/>
       </Unit0>
       <Unit1>
         <Filename Value="tcscanner.pp"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="tcscanner"/>
-        <CursorPos X="27" Y="427"/>
-        <TopLine Value="416"/>
-        <EditorIndex Value="2"/>
+        <WindowIndex Value="1"/>
+        <TopLine Value="1"/>
+        <CursorPos X="17" Y="22"/>
         <UsageCount Value="201"/>
-        <Loaded Value="True"/>
       </Unit1>
       <Unit2>
         <Filename Value="../src/jsbase.pp"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="jsbase"/>
-        <CursorPos X="1" Y="153"/>
-        <TopLine Value="132"/>
-        <EditorIndex Value="1"/>
-        <UsageCount Value="196"/>
-        <Loaded Value="True"/>
+        <WindowIndex Value="1"/>
+        <TopLine Value="1"/>
+        <CursorPos X="1" Y="12"/>
+        <UsageCount Value="200"/>
       </Unit2>
       <Unit3>
         <Filename Value="../src/jsparser.pp"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="jsparser"/>
-        <CursorPos X="1" Y="1"/>
-        <TopLine Value="1"/>
+        <EditorIndex Value="4"/>
+        <WindowIndex Value="1"/>
+        <TopLine Value="2032"/>
+        <CursorPos X="57" Y="2068"/>
         <UsageCount Value="201"/>
+        <Loaded Value="True"/>
       </Unit3>
       <Unit4>
         <Filename Value="../src/jsscanner.pp"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="JSScanner"/>
-        <CursorPos X="1" Y="1"/>
-        <TopLine Value="1"/>
+        <EditorIndex Value="6"/>
+        <WindowIndex Value="1"/>
+        <TopLine Value="342"/>
+        <CursorPos X="76" Y="345"/>
         <UsageCount Value="201"/>
+        <Loaded Value="True"/>
       </Unit4>
       <Unit5>
         <Filename Value="../src/jstree.pp"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="jstree"/>
-        <CursorPos X="1" Y="1"/>
-        <TopLine Value="1"/>
+        <EditorIndex Value="1"/>
+        <WindowIndex Value="1"/>
+        <TopLine Value="838"/>
+        <CursorPos X="3" Y="856"/>
         <UsageCount Value="200"/>
+        <Loaded Value="True"/>
       </Unit5>
       <Unit6>
         <Filename Value="tcparser.pp"/>
         <IsPartOfProject Value="True"/>
         <UnitName Value="tcparser"/>
-        <CursorPos X="3" Y="2059"/>
-        <TopLine Value="2051"/>
-        <EditorIndex Value="4"/>
-        <UsageCount Value="97"/>
+        <EditorIndex Value="5"/>
+        <WindowIndex Value="1"/>
+        <TopLine Value="1"/>
+        <CursorPos X="1" Y="1"/>
+        <UsageCount Value="201"/>
         <Loaded Value="True"/>
       </Unit6>
+      <Unit7>
+        <Filename Value="../src/jswriter.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="jswriter"/>
+        <EditorIndex Value="0"/>
+        <WindowIndex Value="1"/>
+        <TopLine Value="90"/>
+        <CursorPos X="27" Y="120"/>
+        <UsageCount Value="202"/>
+        <Loaded Value="True"/>
+      </Unit7>
+      <Unit8>
+        <Filename Value="tctextwriter.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="tctextwriter"/>
+        <WindowIndex Value="1"/>
+        <TopLine Value="4"/>
+        <CursorPos X="15" Y="22"/>
+        <UsageCount Value="201"/>
+      </Unit8>
+      <Unit9>
+        <Filename Value="../../../../../projects/lazarus/components/fpcunit/console/consoletestrunner.pas"/>
+        <UnitName Value="consoletestrunner"/>
+        <WindowIndex Value="1"/>
+        <TopLine Value="157"/>
+        <CursorPos X="1" Y="175"/>
+        <UsageCount Value="5"/>
+      </Unit9>
+      <Unit10>
+        <Filename Value="tcwriter.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="tcwriter"/>
+        <IsVisibleTab Value="True"/>
+        <EditorIndex Value="3"/>
+        <WindowIndex Value="1"/>
+        <TopLine Value="1822"/>
+        <CursorPos X="3" Y="1841"/>
+        <UsageCount Value="220"/>
+        <Loaded Value="True"/>
+      </Unit10>
+      <Unit11>
+        <Filename Value="../../../../released/packages/fcl-json/src/fpjson.pp"/>
+        <UnitName Value="fpjson"/>
+        <WindowIndex Value="1"/>
+        <TopLine Value="558"/>
+        <CursorPos X="21" Y="580"/>
+        <UsageCount Value="62"/>
+      </Unit11>
+      <Unit12>
+        <Filename Value="../src/jstoken.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="jstoken"/>
+        <EditorIndex Value="2"/>
+        <WindowIndex Value="1"/>
+        <TopLine Value="4"/>
+        <CursorPos X="82" Y="11"/>
+        <UsageCount Value="200"/>
+        <Loaded Value="True"/>
+      </Unit12>
+      <Unit13>
+        <Filename Value="../../../../released/packages/fcl-fpcunit/src/testregistry.pp"/>
+        <UnitName Value="testregistry"/>
+        <WindowIndex Value="1"/>
+        <TopLine Value="106"/>
+        <CursorPos X="22" Y="108"/>
+        <UsageCount Value="14"/>
+      </Unit13>
+      <Unit14>
+        <Filename Value="../../../rtl/tests/punit.pp"/>
+        <UnitName Value="punit"/>
+        <WindowIndex Value="1"/>
+        <TopLine Value="405"/>
+        <CursorPos X="41" Y="415"/>
+        <UsageCount Value="19"/>
+      </Unit14>
+      <Unit15>
+        <Filename Value="../../../../released/rtl/inc/mathh.inc"/>
+        <WindowIndex Value="1"/>
+        <TopLine Value="60"/>
+        <CursorPos X="14" Y="78"/>
+        <UsageCount Value="14"/>
+      </Unit15>
     </Units>
     <JumpHistory Count="30" HistoryIndex="29">
       <Position1>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1752" Column="1" TopLine="1730"/>
+        <Filename Value="../src/jswriter.pp"/>
+        <Caret Line="1017" Column="16" TopLine="1000"/>
       </Position1>
       <Position2>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1767" Column="32" TopLine="1733"/>
+        <Filename Value="../src/jswriter.pp"/>
+        <Caret Line="1024" Column="19" TopLine="1007"/>
       </Position2>
       <Position3>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1790" Column="30" TopLine="1766"/>
+        <Filename Value="../src/jswriter.pp"/>
+        <Caret Line="1027" Column="24" TopLine="1010"/>
       </Position3>
       <Position4>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1763" Column="26" TopLine="1736"/>
+        <Filename Value="tcwriter.pp"/>
+        <Caret Line="161" Column="28" TopLine="142"/>
       </Position4>
       <Position5>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1791" Column="1" TopLine="1758"/>
+        <Filename Value="tcwriter.pp"/>
+        <Caret Line="1463" Column="39" TopLine="1450"/>
       </Position5>
       <Position6>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="114" Column="30" TopLine="93"/>
+        <Filename Value="tcwriter.pp"/>
+        <Caret Line="1617" Column="110" TopLine="1598"/>
       </Position6>
       <Position7>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1806" Column="23" TopLine="1798"/>
+        <Filename Value="tcwriter.pp"/>
+        <Caret Line="162" Column="33" TopLine="143"/>
       </Position7>
       <Position8>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1828" Column="3" TopLine="1819"/>
+        <Filename Value="tcwriter.pp"/>
+        <Caret Line="163" Column="29" TopLine="143"/>
       </Position8>
       <Position9>
-        <Filename Value="../../../../source/fcl-js/jsparser.pp"/>
-        <Caret Line="515" Column="28" TopLine="507"/>
+        <Filename Value="tcwriter.pp"/>
+        <Caret Line="164" Column="36" TopLine="145"/>
       </Position9>
       <Position10>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1824" Column="24" TopLine="1803"/>
+        <Filename Value="tcwriter.pp"/>
+        <Caret Line="165" Column="34" TopLine="146"/>
       </Position10>
       <Position11>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1828" Column="17" TopLine="1803"/>
+        <Filename Value="tcwriter.pp"/>
+        <Caret Line="1727" Column="184" TopLine="1709"/>
       </Position11>
       <Position12>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1849" Column="34" TopLine="1820"/>
+        <Filename Value="tcwriter.pp"/>
+        <Caret Line="166" Column="41" TopLine="147"/>
       </Position12>
       <Position13>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="118" Column="27" TopLine="96"/>
+        <Filename Value="../src/jswriter.pp"/>
+        <Caret Line="1067" Column="1" TopLine="1064"/>
       </Position13>
       <Position14>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1900" Column="29" TopLine="1888"/>
+        <Filename Value="../src/jswriter.pp"/>
+        <Caret Line="1082" Column="15" TopLine="1064"/>
       </Position14>
       <Position15>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1898" Column="12" TopLine="1877"/>
+        <Filename Value="../src/jswriter.pp"/>
+        <Caret Line="1086" Column="29" TopLine="1070"/>
       </Position15>
       <Position16>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1904" Column="80" TopLine="1883"/>
+        <Filename Value="tcwriter.pp"/>
+        <Caret Line="167" Column="1" TopLine="148"/>
       </Position16>
       <Position17>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1914" Column="29" TopLine="1883"/>
+        <Filename Value="../src/jswriter.pp"/>
+        <Caret Line="721" Column="6" TopLine="697"/>
       </Position17>
       <Position18>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1948" Column="18" TopLine="1914"/>
+        <Filename Value="tcwriter.pp"/>
+        <Caret Line="168" Column="30" TopLine="149"/>
       </Position18>
       <Position19>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="20" Column="1" TopLine="1"/>
+        <Filename Value="tcwriter.pp"/>
+        <Caret Line="1770" Column="20" TopLine="1766"/>
       </Position19>
       <Position20>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="2165" Column="1" TopLine="2124"/>
+        <Filename Value="../src/jswriter.pp"/>
+        <Caret Line="114" Column="20" TopLine="78"/>
       </Position20>
       <Position21>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="121" Column="27" TopLine="99"/>
+        <Filename Value="tcwriter.pp"/>
+        <Caret Line="73" Column="1" TopLine="73"/>
       </Position21>
       <Position22>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1969" Column="3" TopLine="1960"/>
+        <Filename Value="tcwriter.pp"/>
+        <Caret Line="2329" Column="1" TopLine="2293"/>
       </Position22>
       <Position23>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1971" Column="46" TopLine="1950"/>
+        <Filename Value="../src/jswriter.pp"/>
+        <Caret Line="1219" Column="6" TopLine="1212"/>
       </Position23>
       <Position24>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1986" Column="14" TopLine="1961"/>
+        <Filename Value="../src/jswriter.pp"/>
+        <Caret Line="1" Column="1" TopLine="1"/>
       </Position24>
       <Position25>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="1993" Column="33" TopLine="1960"/>
+        <Filename Value="../src/jswriter.pp"/>
+        <Caret Line="120" Column="27" TopLine="90"/>
       </Position25>
       <Position26>
-        <Filename Value="../../../../source/fcl-js/jsparser.pp"/>
-        <Caret Line="1702" Column="24" TopLine="1685"/>
+        <Filename Value="../src/jsparser.pp"/>
+        <Caret Line="1771" Column="21" TopLine="1746"/>
       </Position26>
       <Position27>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="124" Column="25" TopLine="100"/>
+        <Filename Value="../src/jsparser.pp"/>
+        <Caret Line="1" Column="1" TopLine="1"/>
       </Position27>
       <Position28>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="2038" Column="10" TopLine="2035"/>
+        <Filename Value="../src/jsparser.pp"/>
+        <Caret Line="103" Column="51" TopLine="73"/>
       </Position28>
       <Position29>
-        <Filename Value="tcparser.pp"/>
-        <Caret Line="2075" Column="56" TopLine="2051"/>
+        <Filename Value="tcwriter.pp"/>
+        <Caret Line="170" Column="40" TopLine="145"/>
       </Position29>
       <Position30>
-        <Filename Value="../../../../source/fcl-js/jsparser.pp"/>
-        <Caret Line="1747" Column="29" TopLine="1740"/>
+        <Filename Value="tcwriter.pp"/>
+        <Caret Line="1808" Column="24" TopLine="1789"/>
       </Position30>
     </JumpHistory>
   </ProjectOptions>
   <CompilerOptions>
-    <Version Value="8"/>
+    <Version Value="11"/>
     <SearchPaths>
-      <IncludeFiles Value="$(ProjOutDir)/"/>
-      <OtherUnitFiles Value="/home/michael/source/fcl-js/;../"/>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <OtherUnitFiles Value="/home/michael/source/fcl-js/;..;../src"/>
     </SearchPaths>
     <CodeGeneration>
       <Optimizations>
         <OptimizationLevel Value="0"/>
       </Optimizations>
     </CodeGeneration>
+    <Linking>
+      <Debugging>
+        <UseHeaptrc Value="True"/>
+      </Debugging>
+    </Linking>
     <Other>
       <CompilerPath Value="$(CompPath)"/>
     </Other>
   </CompilerOptions>
   <Debugging>
-    <BreakPoints Count="1">
+    <BreakPoints Count="7">
       <Item1>
+        <Kind Value="bpkSource"/>
+        <WatchScope Value="wpsGlobal"/>
+        <WatchKind Value="wpkWrite"/>
         <Source Value="../jsscanner.pp"/>
         <Line Value="717"/>
       </Item1>
+      <Item2>
+        <Kind Value="bpkSource"/>
+        <WatchScope Value="wpsLocal"/>
+        <WatchKind Value="wpkWrite"/>
+        <Source Value="tcparser.pp"/>
+        <Line Value="2086"/>
+      </Item2>
+      <Item3>
+        <Kind Value="bpkSource"/>
+        <WatchScope Value="wpsLocal"/>
+        <WatchKind Value="wpkWrite"/>
+        <Source Value="tcparser.pp"/>
+        <Line Value="2566"/>
+      </Item3>
+      <Item4>
+        <Kind Value="bpkSource"/>
+        <WatchScope Value="wpsLocal"/>
+        <WatchKind Value="wpkWrite"/>
+        <Source Value="../src/jsparser.pp"/>
+        <Line Value="845"/>
+      </Item4>
+      <Item5>
+        <Kind Value="bpkSource"/>
+        <WatchScope Value="wpsLocal"/>
+        <WatchKind Value="wpkWrite"/>
+        <Source Value="../src/jsparser.pp"/>
+        <Line Value="754"/>
+      </Item5>
+      <Item6>
+        <Kind Value="bpkSource"/>
+        <WatchScope Value="wpsLocal"/>
+        <WatchKind Value="wpkWrite"/>
+        <Source Value="../src/jsparser.pp"/>
+        <Line Value="1287"/>
+      </Item6>
+      <Item7>
+        <Kind Value="bpkSource"/>
+        <WatchScope Value="wpsLocal"/>
+        <WatchKind Value="wpkWrite"/>
+        <Source Value="tcparser.pp"/>
+        <Line Value="2253"/>
+      </Item7>
     </BreakPoints>
     <Exceptions Count="3">
       <Item1>
@@ -258,4 +397,5 @@
       </Item3>
     </Exceptions>
   </Debugging>
+  <EditorMacros Count="0"/>
 </CONFIG>

+ 6 - 14
packages/fcl-js/tests/testjs.lpr

@@ -3,27 +3,19 @@ program testjs;
 {$mode objfpc}{$H+}
 
 uses
-  Classes, consoletestrunner, tcscanner, jsparser, jsscanner, jstree, jsbase,
-  tcparser;
-
-type
-
-  { TLazTestRunner }
-
-  TMyTestRunner = class(TTestRunner)
-  protected
-  // override the protected methods of TTestRunner to customize its behavior
-  end;
+  cwstring,Classes, consoletestrunner, tcscanner, jsparser, jsscanner, jstree, jsbase,
+  tcparser, jswriter, tctextwriter, tcwriter, jstoken;
 
 var
-  Application: TMyTestRunner;
+  Application: TTestRunner;
 
 {$IFDEF WINDOWS}{$R testjs.rc}{$ENDIF}
 
 begin
-  Application := TMyTestRunner.Create(nil);
+  DefaultFormat:=fplain;
+  DefaultRunAllTests:=True;
+  Application := TTestRunner.Create(nil);
   Application.Initialize;
-  Application.Title := 'FPCUnit Console test runner';
   Application.Run;
   Application.Free;
 end.