Преглед на файлове

* TypeScript Improvements, 97% of DefinitelyTyped declarations now parsed

Michaël Van Canneyt преди 3 години
родител
ревизия
fa9d77e67e

+ 202 - 0
packages/fcl-js/examples/parsefiles.pas

@@ -0,0 +1,202 @@
+{ *********************************************************************
+    This file is part of the Free Component Library (FCL)
+    Copyright (c) 2021 Michael Van Canneyt.
+
+    Javascript & typescript parser demo
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+  **********************************************************************}
+
+program parsefiles;
+
+{$mode objfpc}{$H+}
+
+uses
+  Classes, SysUtils, CustApp, Math, jsTree, jsScanner, jsParser;
+
+type
+  TCounts = Record
+    Total,OK,Failed : Integer;
+    Stop : Boolean;
+  end;
+
+  { TParseTSApplication }
+
+  TParseTSApplication = class(TCustomApplication)
+  private
+    FTypescript : Boolean;
+    FStopOnError : Boolean;
+    function ParseFile(const aFileName: string): Boolean;
+    function ParseFiles(const aDirectory: string; RecurseLevel : Integer): TCounts;
+  protected
+    procedure DoRun; override;
+  public
+    constructor Create(TheOwner: TComponent); override;
+    destructor Destroy; override;
+    procedure Usage(Msg : string); virtual;
+  end;
+
+{ TParseTSApplication }
+
+procedure TParseTSApplication.DoRun;
+
+var
+  ErrorMsg: String;
+  Directory : String;
+  Counts : TCounts;
+
+begin
+  Terminate;
+  ErrorMsg:=CheckOptions('hd:srt', ['help','directory','stop-on-error','recurse','typescript']);
+  if (ErrorMsg<>'') or HasOption('h','help') then
+    begin
+    Usage(ErrorMsg);
+    Exit;
+    end;
+  FTypescript:=HasOption('t','typescript');
+  Directory:=GetOptionValue('d','directory');
+  FStopOnError:=HasOption('s','stop-on-error');
+  If Directory='' then
+    Directory:=GetCurrentDir;
+  Counts:=ParseFiles(Directory,Ord(HasOption('r','recurse')));
+  With Counts do
+    Writeln('Statistics: ',Total,' Total, ',OK,' OK, ',Failed, ' Failed');
+end;
+
+Function TParseTSApplication.ParseFile(const aFileName : string) : Boolean;
+
+Var
+  aFile : TStrings;
+  P : TJSParser;
+  S : TStringStream;
+  el : TJSElement;
+  I : Integer;
+  EP : EJSParser;
+  Prefix : String;
+
+begin
+  Result:=False;
+  Writeln('Parsing: ',aFileName);
+  Flush(output);
+  el:=nil;
+  S:=Nil;
+  aFile:=TStringList.Create;
+  try
+    aFile.LoadFromFile(aFileName);
+    S:=TStringStream.Create('');
+    S.LoadFromFile(aFileName);
+    P:=TJSParser.Create(S,ecma2021,FTypescript);
+    try
+      El:=P.Parse;
+      Result:=True;
+    except
+      On E : Exception do
+        begin
+        writeln('Error ',E.ClassName,' parsing file ',aFileName,' : ',E.Message);
+        if E is EJSParser then
+          begin
+          Writeln('Offending lines : ');
+          EP:=EJSParser(E);
+          For I:=Max(1,EP.ErrorRow-1) to Min(aFile.Count,EP.ErrorRow+1) do
+            begin
+            Prefix:=IntToStr(I);
+            Writeln(Prefix,' : ',aFile[I-1]);
+            if I=EP.ErrorRow then
+              Writeln(StringOfChar(' ',EP.ErrorCol-1),StringOfChar('-',Length(Prefix)+3),'^');
+            end;
+          end;
+        end;
+    end;
+  finally
+    el.Free;
+    aFile.Free;
+    S.Free;
+  end;
+end;
+
+Function TParseTSApplication.ParseFiles(Const aDirectory : string; RecurseLevel : Integer) : TCounts;
+
+Var
+  Info : TSearchRec;
+  Res: TCounts;
+  Ext : string;
+begin
+  Result:=Default(TCounts);
+  if FTypeScript then
+    Ext:='*.d.ts'
+  else
+    Ext:='*.js';
+  If FindFirst(aDirectory+Ext,0,Info)=0 then
+    try
+      Repeat
+        Inc(Result.Total);
+        if ParseFile(aDirectory+Info.Name) then
+          Inc(Result.OK)
+        else
+          begin
+          Inc(Result.Failed);
+          if FStopOnError then
+            Result.Stop:=True;
+          end;
+      until (FindNext(Info)<>0) or Result.Stop;
+    Finally
+      FindClose(Info)
+    end;
+  if RecurseLevel=0 then
+    exit;
+  If FindFirst(aDirectory+'*',faDirectory,Info)=0 then
+    try
+      Repeat
+        if (Info.Attr and faDirectory)=faDirectory then
+          begin
+          Res:=ParseFiles(aDirectory+Info.Name+PathDelim,RecurseLevel-1);
+          Result.Total:=Result.Total+Res.Total;
+          Result.OK:=Result.OK+Res.OK;
+          Result.Failed:=Result.Failed+Res.Failed;
+          Result.Stop:=Result.Stop or Res.Stop
+          end
+      until (FindNext(Info)<>0) or Res.Stop;
+    Finally
+      FindClose(Info)
+    end;
+end;
+
+constructor TParseTSApplication.Create(TheOwner: TComponent);
+begin
+  inherited Create(TheOwner);
+  StopOnException:=True;
+end;
+
+destructor TParseTSApplication.Destroy;
+begin
+  inherited Destroy;
+end;
+
+procedure TParseTSApplication.Usage(Msg: string);
+begin
+  if Msg<>'' then
+    Writeln('Error : ',Msg);
+  writeln('Usage: ', ExeName, ' [options]');
+  Writeln('Where options is one or mote of:');
+  Writeln('-h --help           Display this help text');
+  Writeln('-d --directory      Parse all files in directory');
+  Writeln('-s --stop-on-error  Stop parsing files after an error');
+  Writeln('-t --typescript     Assume typscript');
+
+end;
+
+var
+  Application: TParseTSApplication;
+begin
+  Application:=TParseTSApplication.Create(nil);
+  Application.Title:='My Application';
+  Application.Run;
+  Application.Free;
+end.
+

Файловите разлики са ограничени, защото са твърде много
+ 516 - 120
packages/fcl-js/src/jsparser.pp


+ 22 - 5
packages/fcl-js/src/jsscanner.pp

@@ -19,7 +19,7 @@ unit JSScanner;
 
 interface
 
-uses SysUtils, Classes, jstoken;
+uses SysUtils, Classes, jsbase, jstoken;
 
 Type
   TECMAVersion = (ecma5,ecma2015,ecma2021);
@@ -37,6 +37,7 @@ Const
 resourcestring
   SErrInvalidCharacter = 'Invalid character ''%s''';
   SErrOpenString = 'string exceeds end of line';
+  SErrExpectedEllipsis = 'Expected ellipsis, got ..';
   SErrIncludeFileNotFound = 'Could not find include file ''%s''';
   SErrIfXXXNestingLimitReached = 'Nesting of $IFxxx too deep';
   SErrInvalidPPElse = '$ELSE without matching $IFxxx';
@@ -84,6 +85,7 @@ Type
 
   TJSScanner = class
   private
+    FDisableRShift: Boolean;
     FECMAVersion: TECMAVersion;
     FIsTypeScript: Boolean;
     FReturnComments: Boolean;
@@ -92,7 +94,7 @@ Type
     FSourceFilename: string;
     FCurRow: Integer;
     FCurToken: TJSToken;
-    FCurTokenString: string;
+    FCurTokenString: String;
     FCurLine: string;
     TokenStr: PChar;
     FWasEndOfLine : Boolean;
@@ -132,9 +134,10 @@ Type
     property CurRow: Integer read FCurRow;
     property CurColumn: Integer read GetCurColumn;
     property CurToken: TJSToken read FCurToken;
-    property CurTokenString: string read FCurTokenString;
+    property CurTokenString: String read FCurTokenString;
     property ECMAVersion : TECMAVersion Read FECMAVersion Write SetECMAVersion;
     Property IsTypeScript : Boolean Read FIsTypeScript Write SetIsTypeScript;
+    Property DisableRShift : Boolean Read FDisableRShift Write FDisableRShift;
   end;
 
 
@@ -475,8 +478,10 @@ function TJSScanner.DoNumericLiteral :TJSToken;
 Var
   TokenStart : PChar;
   Len : Integer;
+  Hex : Boolean;
 
 begin
+  Hex:=False;
   TokenStart := TokenStr;
   while true do
     begin
@@ -488,6 +493,7 @@ begin
           Inc(TokenStr);
           while Upcase(TokenStr[0]) in ['0'..'9','A'..'F'] do
             Inc(TokenStr);
+          Break;  
           end
         else
           Error(SInvalidHexadecimalNumber);
@@ -519,7 +525,7 @@ begin
   Len:=TokenStr-TokenStart;
   Setlength(FCurTokenString, Len);
   if (Len>0) then
-  Move(TokenStart^,FCurTokenString[1],Len);
+    Move(TokenStart^,FCurTokenString[1],Len);
   Result := tjsNumber;
 end;
 
@@ -711,6 +717,17 @@ begin
         If (Result=tjsNumber) then
           FCurTokenString:='0.'+FCurTokenString;
          end
+      else if TokenStr[0] = '.' then
+        begin
+        Inc(TokenStr);
+        if TokenStr[0]='.' then
+          begin
+          Inc(TokenStr);
+          Result:=tjsEllipsis
+          end
+        else
+          Error(SerrExpectedEllipsis);
+        end
       else
         Result := tjsDot;
       end;
@@ -803,7 +820,7 @@ begin
         Inc(TokenStr);
         Result:=tjsGE;
         end
-      else if TokenStr[0] = '>' then
+      else if (TokenStr[0] = '>') and Not DisableRShift then
   	begin
         Inc(TokenStr);
         if (TokenStr[0] = '>') then

+ 2 - 2
packages/fcl-js/src/jstoken.pp

@@ -36,7 +36,7 @@ type
      tjsPLUSEQ, tjsPLUSPLUS,
      tjsURSHIFT, tjsURSHIFTEQ,
      tjsRSHIFT, tjsRSHIFTEQ,
-     tjsSEQ, tjsSNE, tjsMULEQ, tjsArrow,
+     tjsSEQ, tjsSNE, tjsMULEQ, tjsArrow, tjsEllipsis,
      { Reserved words start here. They must be last }
      tjsAWAIT, tjsBREAK, tjsCASE, tjsCATCH, tjsCLASS, tjsCONST, tjsCONTINUE,
      tjsDEBUGGER, tjsDEFAULT, tjsDELETE, tjsDO,
@@ -74,7 +74,7 @@ const
         '+=', '++',
         '>>>', '>>>=',
         '>>', '>>=',
-        '===', '!==', '*=', '=>',
+        '===', '!==', '*=', '=>', '...',
         // Identifiers last
         'await', 'break','case','catch', 'class','const','continue',
      'debugger','default','delete', 'do',

Файловите разлики са ограничени, защото са твърде много
+ 449 - 75
packages/fcl-js/src/jstree.pp


+ 1 - 1
packages/fcl-js/src/jswriter.pp

@@ -2005,7 +2005,7 @@ Const
 begin
   Write(Keywords[el.varType]+' ');
   FSkipRoundBrackets:=true;
-  WriteJS(El.A);
+  WriteJS(El.VarDecl);
 end;
 
 procedure TJSWriter.WriteJS(El: TJSElement);

+ 54 - 63
packages/fcl-js/src/tstopas.pp

@@ -164,9 +164,6 @@ Type
     function WriteInterfaceDef(Intf: TIDLInterfaceDefinition): Boolean; virtual;
     function WriteDictionaryDef(aDict: TIDLDictionaryDefinition): Boolean; virtual;
     // Additional
-    procedure WritePromiseDef(aDef: TIDLPromiseTypeDefDefinition);virtual;
-    procedure WriteSequenceDef(aDef: TIDLSequenceTypeDefDefinition);virtual;
-    procedure WriteUnionDef(aDef: TIDLUnionTypeDefDefinition);virtual;
     }
     // Properties
     procedure WritePropertyDeclaration(D: TJSVariableStatement);
@@ -180,6 +177,7 @@ Type
     function GetAliasTypeAsString(aTypeDef: TJSTypeReference; asPascal, asSubType: Boolean): string;
     function GetIntersectionTypeAsString(aTypeDef: TJSIntersectionTypeDef; asPascal, asSubType: Boolean): String;
     function GetUnionTypeAsString(aTypeDef: TJSUnionTypeDef; asPascal, asSubType: Boolean): String;
+    function GetEnumTypeAsString(aTypeDef: TJSEnumTypeDef; asPascal, asSubType: Boolean): String;
 
     // Write types
     procedure WriteTypeDefs(Types: TJSElementNodes); virtual;
@@ -188,6 +186,7 @@ Type
     procedure WriteUnionTypeDef(const aPasName: string; const aOrgName: jsBase.TJSString; aTypeParams: TJSElementNodes; aTypeDef: TJSUnionTypeDef);
     procedure WriteIntersectionTypeDef(const aPasName: string; const aOrgName: jsBase.TJSString; aTypeParams: TJSElementNodes; aTypeDef: TJSIntersectionTypeDef);
     procedure WriteArrayTypeDef(const aPasName: string; const aOrgName: jsBase.TJSString; aTypeParams: TJSElementNodes;  aTypeDef: TJSArrayTypeDef);
+    procedure WriteEnumTypeDef(const aPasName: string; const aOrgName: jsBase.TJSString; aTypeParams: TJSElementNodes;  aTypeDef: TJSEnumTypeDef);
 
     // Extra interface/Implementation code.
     procedure WriteImplementation; virtual;
@@ -317,7 +316,7 @@ end;
 
 destructor TPasData.destroy;
 begin
-  Writeln('Destroying ',Self.FOriginalName,'->',Self.Pasname);
+  // Writeln('Destroying ',Self.FOriginalName,'->',Self.Pasname);
   inherited destroy;
 end;
 
@@ -396,7 +395,7 @@ begin
       Raise ETStoPas.Create('Parse result is not a function body');
       end;
     FElements:=El as TJSFunctionBody;
-    // DumpElements;
+    DumpElements;
   finally
     P.Free;
     S.Free;
@@ -1079,56 +1078,12 @@ begin
   Addln('%s = array of %s;',[GetName(aDef),GetTypeName(aDef.ElementType)])
 end;
 
-
-procedure TTypescriptToPas.WriteUnionDef(aDef : TIDLUnionTypeDefDefinition);
-
-Var
-  S : UTF8String;
-  D : TIDLDefinition;
-begin
-  S:='';
-  For D in adef.Union do
-    begin
-    if (S<>'') then
-      S:=S+', ';
-    S:=S+(D as TIDLTypeDefDefinition).TypeName;
-    end;
-  Comment('Union of '+S);
-  AddLn('%s = JSValue; ',[GetName(aDef)])
-end;
-
-
 procedure TTypescriptToPas.WritePromiseDef(aDef : TIDLPromiseTypeDefDefinition);
 
 begin
   AddLn('%s = TJSPromise;',[GetName(aDef)]);
 end;
 
-procedure TTypescriptToPas.WriteAliasTypeDef(aDef : TJSTypeDeclaration);
-
-Var
-  TN : String;
-
-begin
-  TN:=GetTypeName((aDef.TypeDef as TJSTypeReference).Name,True);
-  AddLn('%s = %s;',[GetName(aDef),TN]);
-end;
-
-function TTypescriptToPas.WriteTypeDef(aDef: TJSTypeDeclaration): Boolean;
-
-begin
-  Result:=True;
-  if ADef is TIDLSequenceTypeDefDefinition then
-    WriteSequenceDef(aDef as TIDLSequenceTypeDefDefinition)
-  else if ADef is TIDLUnionTypeDefDefinition then
-    WriteUnionDef(aDef as TIDLUnionTypeDefDefinition)
-  else if ADef is TIDLPromiseTypeDefDefinition then
-    WritePromiseDef(aDef as TIDLPromiseTypeDefDefinition)
-  else if ADef is TIDLRecordDefinition then
-    WriteRecordDef(aDef as TIDLRecordDefinition)
-  else
-    WriteAliasTypeDef(aDef);
-end;
 
 function TTypescriptToPas.WriteRecordDef(aDef: TIDLRecordDefinition): Boolean;
 
@@ -1617,11 +1572,12 @@ begin
     if coaddOptionsToheader in Options then
       AddOptionsToHeader;
     end;
-  if SourceElements.Types.Count>0 then
+  if (SourceElements.Types.Count>0) or (SourceElements.Enums.Count>0) then
     begin
     EnsureSection(csType);
     Indent;
     WriteTypeDefs(SourceElements.Types);
+    WriteTypeDefs(SourceElements.Enums);
     {
     WriteForwardClassDefs(Context.Definitions);
     WriteEnumDefs(Context.Definitions);
@@ -1743,14 +1699,10 @@ end;
 
 procedure TTypescriptToPas.AllocatePasNames(aList : TJSSourceElements; ParentName: String = '');
 
-Var
-  I : Integer;
-
 begin
-  For I:=0 to aList.Types.Count-1 do
-    AllocatePasNames(aList.Types,ParentName);
-  For I:=0 to aList.Vars.Count-1 do
-    AllocatePasNames(aList.Vars,ParentName);
+  AllocatePasNames(aList.Types,ParentName);
+  AllocatePasNames(aList.Enums,ParentName);
+  AllocatePasNames(aList.Vars,ParentName);
 end;
 
 
@@ -1807,6 +1759,8 @@ begin
     Result:=GetIntersectionTypeAsString(TJSIntersectionTypeDef(aType),asPascal,asSubType)
   else if aType is TJSArrayTypeDef then
     Result:=GetArrayTypeAsString(TJSArrayTypeDef(aType),asPascal,asSubType)
+  else if aType is TJSEnumTypeDef then
+    Result:=GetEnumTypeAsString(TJSEnumTypeDef(aType),asPascal,asSubType)
 end;
 
 Function TTypescriptToPas.GetUnionTypeAsString(aTypeDef : TJSUnionTypeDef; asPascal,asSubType : Boolean) : String;
@@ -1819,13 +1773,34 @@ begin
   For I:=0 to aTypeDef.TypeCount-1 do
     begin
     if Result<>'' then
-      Result:=Result+'&';
-    GetTypeAsString(aTypeDef.Types[I],asPascal,True);
+      Result:=Result+' | ';
+    Result:=Result+GetTypeAsString(aTypeDef.Types[I],asPascal,True);
     end;
   if AsSubType then
     Result:='('+Result+')';
 end;
 
+function TTypescriptToPas.GetEnumTypeAsString(aTypeDef: TJSEnumTypeDef; asPascal, asSubType: Boolean): String;
+Var
+  I : Integer;
+  N : String;
+
+begin
+  Result:='';
+  For I:=0 to aTypeDef.NameCount-1 do
+    begin
+    if Result<>'' then
+      Result:=Result+', ';
+    N:=aTypeDef.Names[I];
+    if IsKeyWord(N) then
+      N:='&'+N;
+    Result:=Result+N;
+    end;
+  Result:='('+Result+')';
+  if AsSubType then
+    Result:='('+Result+')';
+end;
+
 Function TTypescriptToPas.GetIntersectionTypeAsString(aTypeDef : TJSIntersectionTypeDef; asPascal,asSubType : Boolean) : String;
 
 Var
@@ -1836,8 +1811,8 @@ begin
   For I:=0 to aTypeDef.TypeCount-1 do
     begin
     if Result<>'' then
-      Result:=Result+'|';
-    GetTypeAsString(aTypeDef.Types[I],asPascal,True);
+      Result:=Result+' & ';
+    Result:=Result+GetTypeAsString(aTypeDef.Types[I],asPascal,True);
     end;
   if AsSubType then
     Result:='('+Result+')';
@@ -1867,7 +1842,7 @@ begin
   genparams:=GetGenericParams(aTypeParams);
   if (genparams<>'') then
     gen:='generic ';
-  AddLn('%s%s%s = %s; // Intersection type: %s',[gen,aPasName,genparams,TN,GetTypeAsString(aTypeDef,False,false)]);
+  AddLn('%s%s%s = %s; // %s',[gen,aPasName,genparams,TN,GetTypeAsString(aTypeDef,False,false)]);
 end;
 
 Procedure TTypescriptToPas.WriteArrayTypeDef(const aPasName : string; const aOrgName : jsBase.TJSString; aTypeParams: TJSElementNodes;aTypeDef : TJSArrayTypeDef);
@@ -1884,6 +1859,19 @@ begin
   AddLn('%s%s%s = %s;',[gen,aPasName,genparams,arr]);
 end;
 
+procedure TTypescriptToPas.WriteEnumTypeDef(const aPasName: string; const aOrgName: jsBase.TJSString; aTypeParams: TJSElementNodes;
+  aTypeDef: TJSEnumTypeDef);
+var
+ arr,gen, genparams: String;
+
+begin
+  genparams:=GetGenericParams(aTypeParams);
+  if (genparams<>'') then
+    gen:='generic ';
+  arr:=GetEnumTypeAsString(aTypeDef,True,False);
+  AddLn('%s%s%s = %s;',[gen,aPasName,genparams,arr]);
+end;
+
 
 Procedure TTypescriptToPas.WriteTypeDef(const aPasName : string; const aOrgName : jsBase.TJSString; aTypeParams: TJSElementNodes; aTypeDef : TJSTypeDef);
 
@@ -1896,6 +1884,8 @@ begin
     WriteIntersectionTypeDef(aPasName,aOrgName,aTypeParams,TJSIntersectionTypeDef(aTypeDef))
   else if aTypeDef is TJSArrayTypeDef then
     WriteArrayTypeDef(aPasName,aOrgName,aTypeParams,TJSArrayTypeDef(aTypeDef))
+  else if aTypeDef is TJSEnumTypeDef then
+    WriteEnumTypeDef(aPasName,aOrgName,aTypeParams,TJSEnumTypeDef(aTypeDef))
 end;
 
 Procedure TTypescriptToPas.WriteTypeDefs(Types: TJSElementNodes);
@@ -1911,11 +1901,12 @@ begin
   for I:=0 to Types.Count-1 do
     begin
     N:=Types[0].Node;
+    // TJSEnumDeclaration is a descendent
     if N is TJSTypeDeclaration then
       begin
       aName:=GetName(Decl);
       WriteTypeDef(aName, Decl.Name, Decl.TypeParams, Decl.TypeDef);
-      end;
+      end
     end;
 end;
 

+ 104 - 51
packages/fcl-js/tests/tcparser.pp

@@ -29,13 +29,14 @@ type
     Procedure AssertEquals(Const AMessage : String; Expected, Actual : TJSType); overload;
     Procedure AssertEquals(Const AMessage : String; Expected, Actual : TJSVarType); overload;
     Procedure AssertIdentifier(Msg : String; El : TJSElement; Const AName : TJSString);
+    procedure AssertEquals(Const AMessage : String; aExpected : AnsiString; aActual : TJSString); overload;
     Function  GetSourceElements : TJSSourceElements;
     Function  GetVars : TJSElementNodes;
     Function  GetStatements : TJSElementNodes;
     Function  GetFunctions : TJSElementNodes;
     Function  GetFirstFunction : TJSFunctionDeclarationStatement;
     Function  GetClasses : TJSElementNodes;
-    Function  GetFirstClass : TJSClassDeclaration;
+    Function  GetFirstClass(isAmbient : Boolean = false) : TJSClassDeclaration;
     Function  GetFirstStatement : TJSElement;
     Function  GetFirstVar : TJSElement;
     Function  GetModules : TJSElementNodes;
@@ -47,6 +48,7 @@ type
     Function  GetEnums : TJSElementNodes;
     Function  GetFirstEnum: TJSEnumDeclaration;
     Function  GetExpressionStatement : TJSExpressionStatement;
+    Function  GetInterfaces : TJSElementNodes;
     Function  GetFirstInterface : TJSInterfaceDeclaration;
   end;
 
@@ -135,6 +137,7 @@ type
     procedure TestFunctionDeclarationEmpty;
     procedure TestFunctionDeclarationAsync;
     procedure TestFunctionDeclarationWithArgs;
+    procedure TestFunctionDeclarationWithSpreadArgs;
     procedure TestFunctionDeclarationWithBody;
     procedure TestIfSimple;
     procedure TestIfElseSimple;
@@ -295,7 +298,7 @@ begin
   Result:=GetSourceElements.Classes;
 end;
 
-function TTestBaseJSParser.GetFirstClass: TJSClassDeclaration;
+function TTestBaseJSParser.GetFirstClass(isAmbient : Boolean = false): TJSClassDeclaration;
 
 Var
   aClasses : TJSElementNodes;
@@ -304,7 +307,10 @@ begin
   aClasses:=GetClasses;
   AssertTrue('Have classes ',aClasses.Count>0);
   AssertNotNull('have first class node',aClasses.Nodes[0].Node);
-  AssertEquals('First class node is class declaration',TJSClassDeclaration,aClasses.Nodes[0].Node.ClassType);
+  if IsAmbient then
+    AssertEquals('First class node is ambientclass declaration',TJSAmbientClassDeclaration,aClasses.Nodes[0].Node.ClassType)
+  else
+    AssertEquals('First class node is class declaration',TJSClassDeclaration,aClasses.Nodes[0].Node.ClassType);
   Result:=TJSClassDeclaration(aClasses.Nodes[0].Node);
 end;
 
@@ -336,7 +342,7 @@ Procedure TTestBaseJSParser.AssertIdentifier(Msg: String; El: TJSElement;
 
 Var
   L : TJSPrimaryExpressionIdent;
-  S1,S2 : String;
+  S1,S2 : TJSString;
 begin
   AssertNotNull(Msg+' have TJSPrimaryExpressionIdent element',El);
   CheckClass(El,TJSPrimaryExpressionIdent);
@@ -346,6 +352,11 @@ begin
   AssertEquals(Msg+'Identifier has correct name',S2,S1);
 end;
 
+procedure TTestBaseJSParser.AssertEquals(const AMessage: String; aExpected: AnsiString; aActual: TJSString);
+begin
+  AssertEquals(AMessage,UTF8Decode(aExpected),aActual);
+end;
+
 Function TTestBaseJSParser.GetFirstStatement: TJSElement;
 
 Var
@@ -353,7 +364,7 @@ Var
 begin
   E:=GetStatements;
   AssertNotNull('Have statements',E);
-  AssertEquals('1 statement',1,E.Count);
+  AssertTrue('1 statement',1<=E.Count);
   Result:=E.Nodes[0].Node;
   AssertNotNull('First statement assigned',Result);
 end;
@@ -450,15 +461,20 @@ begin
   Result:=TJSExpressionStatement(N);
 end;
 
+function TTestBaseJSParser.GetInterfaces: TJSElementNodes;
+begin
+  Result:=GetSourceElements.Interfaces;
+end;
+
 function TTestBaseJSParser.GetFirstInterface: TJSInterfaceDeclaration;
 Var
   E : TJSElementNodes;
 
 begin
-  E:=GetTypes;
-  AssertNotNull('Have types',E);
-  AssertEquals('1 type',1,E.Count);
-  AssertEquals('First type node is type declaration',TJSInterfaceDeclaration,E.Nodes[0].Node.ClassType);
+  E:=GetInterfaces;
+  AssertNotNull('Have interfaces',E);
+  AssertEquals('1 interfaces',1,E.Count);
+  AssertEquals('First interface node is interface declaration',TJSInterfaceDeclaration,E.Nodes[0].Node.ClassType);
   Result:=(E.Nodes[0].Node as TJSInterfaceDeclaration);
 end;
 
@@ -2050,7 +2066,7 @@ begin
   CheckClass(X,TJSExpressionStatement);
   CheckNotNull(TJSExpressionStatement(X).A);
   CheckClass(TJSExpressionStatement(X).A,TJSPrimaryExpressionIdent);
-  AssertEquals('a',TJSPrimaryExpressionIdent(TJSExpressionStatement(X).A).Name)
+  AssertEquals('Name','a',TJSPrimaryExpressionIdent(TJSExpressionStatement(X).A).Name)
 end;
 
 procedure TTestJSParser.TestFunctionDeclarationEmpty;
@@ -2139,6 +2155,38 @@ begin
 //  TJSEmptyBlockStatement
 end;
 
+procedure TTestJSParser.TestFunctionDeclarationWithSpreadArgs;
+Var
+  E : TJSSourceElements;
+  N : TJSElement;
+  FD : TJSFunctionDeclarationStatement;
+  P : TJSTypedParam;
+
+begin
+  CreateParser('function a (...b,c) {}');
+  E:=GetSourceElements;
+  AssertEquals('1 function defined',1,E.functions.Count);
+  N:=E.Functions.Nodes[0].Node;
+  AssertNotNull('Function element defined ',N);
+  CheckClass(N,TJSFunctionDeclarationStatement);
+  FD:=TJSFunctionDeclarationStatement(N);
+  AssertNotNull('Function definition assigned',FD.AFunction);
+  AssertEquals('Function name OK','a',FD.AFunction.Name);
+  AssertNotNull('Function body assigned', FD.AFunction.Body);
+  AssertEquals('2 parameters',2,FD.AFunction.Params.Count);
+  AssertEquals('1st parameter','b',FD.AFunction.Params[0]);
+  AssertEquals('1st parameter','b',FD.AFunction.TypedParams.Names[0]);
+  AssertTrue('1st parameter spread',FD.AFunction.TypedParams.Params[0].IsSpread);
+  AssertEquals('2nd parameter','c',FD.AFunction.Params[1]);
+  N:=FD.AFunction.Body;
+  CheckClass(N,TJSFunctionBody);
+  AssertNotNull('Function body has element',TJSFunctionBody(N).A);
+  CheckClass(TJSFunctionBody(N).A,  TJSSourceElements);
+  E:=TJSSourceElements(TJSFunctionBody(N).A);
+  AssertEquals('0 statement in functionbody elements',0,E.Statements.Count);
+//  TJSEmptyBlockStatement
+end;
+
 procedure TTestJSParser.TestFunctionDeclarationWithBody;
 
 Var
@@ -2168,7 +2216,7 @@ begin
   CheckClass(N,TJSExpressionStatement);
   CheckNotNull(TJSExpressionStatement(N).A);
   CheckClass(TJSExpressionStatement(N).A,TJSPrimaryExpressionIdent);
-  AssertEquals('b',TJSPrimaryExpressionIdent(TJSExpressionStatement(N).A).Name);
+  AssertEquals('Name','b',TJSPrimaryExpressionIdent(TJSExpressionStatement(N).A).Name);
 //  TJSEmptyBlockStatement
 end;
 
@@ -2185,13 +2233,13 @@ begin
   I:=TJSIfStatement(E);
   AssertNotNull('Statement condition assigned',I.Cond);
   CheckClass(I.Cond,TJSPrimaryExpressionIdent);
-  AssertEquals('a',TJSPrimaryExpressionIdent(I.Cond).Name);
+  AssertEquals('Name 1','a',TJSPrimaryExpressionIdent(I.Cond).Name);
   AssertNull('Statement false branch assigned',I.BFalse);
   AssertNotNull('Statement true branch assigned',I.Btrue);
   CheckClass(I.Btrue,TJSExpressionStatement);
   AssertNotNull('Expression statement expression',TJSExpressionStatement(I.BTrue).A);
   CheckClass(TJSExpressionStatement(I.BTrue).A,TJSPrimaryExpressionIdent);
-  AssertEquals('b',TJSPrimaryExpressionIdent(TJSExpressionStatement(I.Btrue).A).Name);
+  AssertEquals('Name 2','b',TJSPrimaryExpressionIdent(TJSExpressionStatement(I.Btrue).A).Name);
 end;
 
 procedure TTestJSParser.TestIfEmptyBlock;
@@ -2207,7 +2255,7 @@ begin
   I:=TJSIfStatement(E);
   AssertNotNull('Statement condition assigned',I.Cond);
   CheckClass(I.Cond,TJSPrimaryExpressionIdent);
-  AssertEquals('a',TJSPrimaryExpressionIdent(I.Cond).Name);
+  AssertEquals('Name 1','a',TJSPrimaryExpressionIdent(I.Cond).Name);
   AssertNull('Statement false branch assigned',I.BFalse);
   AssertNotNull('Statement true branch assigned',I.Btrue);
   CheckClass(I.Btrue,TJSEmptyBlockStatement);
@@ -2226,7 +2274,7 @@ begin
   I:=TJSIfStatement(E);
   AssertNotNull('Statement condition assigned',I.Cond);
   CheckClass(I.Cond,TJSPrimaryExpressionIdent);
-  AssertEquals('a',TJSPrimaryExpressionIdent(I.Cond).Name);
+  AssertEquals('Name 1','a',TJSPrimaryExpressionIdent(I.Cond).Name);
   AssertNotNull('Statement false branch assigned',I.BFalse);
   AssertNotNull('Statement true branch assigned',I.Btrue);
   CheckClass(I.Btrue,TJSEmptyBlockStatement);
@@ -2244,12 +2292,12 @@ begin
   W:=TJSWhileStatement(E);
   AssertNotNull('Statement condition assigned',W.Cond);
   CheckClass(W.Cond,TJSPrimaryExpressionIdent);
-  AssertEquals('a',TJSPrimaryExpressionIdent(W.Cond).Name);
+  AssertEquals('Name 1','a',TJSPrimaryExpressionIdent(W.Cond).Name);
   AssertNotNull('Statement condition assigned',W.body);
   CheckClass(W.Body,TJSExpressionStatement);
   AssertNotNull('Expression statement expression',TJSExpressionStatement(W.Body).A);
   CheckClass(TJSExpressionStatement(W.Body).A,TJSPrimaryExpressionIdent);
-  AssertEquals('b',TJSPrimaryExpressionIdent(TJSExpressionStatement(W.Body).A).Name);
+  AssertEquals('Name 1','b',TJSPrimaryExpressionIdent(TJSExpressionStatement(W.Body).A).Name);
 end;
 
 procedure TTestJSParser.TestWhileBlock;
@@ -2266,12 +2314,12 @@ begin
   W:=TJSWhileStatement(E);
   AssertNotNull('Statement condition assigned',W.Cond);
   CheckClass(W.Cond,TJSPrimaryExpressionIdent);
-  AssertEquals('a',TJSPrimaryExpressionIdent(W.Cond).Name);
+  AssertEquals('Name 1','a',TJSPrimaryExpressionIdent(W.Cond).Name);
   AssertNotNull('Statement condition assigned',W.body);
   CheckClass(W.Body,TJSExpressionStatement);
   AssertNotNull('Expression statement expression',TJSExpressionStatement(W.Body).A);
   CheckClass(TJSExpressionStatement(W.Body).A,TJSPrimaryExpressionIdent);
-  AssertEquals('b',TJSPrimaryExpressionIdent(TJSExpressionStatement(W.Body).A).Name);
+  AssertEquals('Name 1','b',TJSPrimaryExpressionIdent(TJSExpressionStatement(W.Body).A).Name);
 end;
 
 procedure TTestJSParser.TestDoWhileSimple;
@@ -2288,12 +2336,12 @@ begin
   W:=TJSDoWhileStatement(E);
   AssertNotNull('Statement condition assigned',W.Cond);
   CheckClass(W.Cond,TJSPrimaryExpressionIdent);
-  AssertEquals('a',TJSPrimaryExpressionIdent(W.Cond).Name);
+  AssertEquals('Name 1','a',TJSPrimaryExpressionIdent(W.Cond).Name);
   AssertNotNull('Statement condition assigned',W.body);
   CheckClass(W.Body,TJSExpressionStatement);
   AssertNotNull('Expression statement expression',TJSExpressionStatement(W.Body).A);
   CheckClass(TJSExpressionStatement(W.Body).A,TJSPrimaryExpressionIdent);
-  AssertEquals('b',TJSPrimaryExpressionIdent(TJSExpressionStatement(W.Body).A).Name);
+  AssertEquals('Name 1','b',TJSPrimaryExpressionIdent(TJSExpressionStatement(W.Body).A).Name);
 end;
 
 procedure TTestJSParser.TestDoWhileBlock;
@@ -2310,12 +2358,12 @@ begin
   W:=TJSDoWhileStatement(E);
   AssertNotNull('Statement condition assigned',W.Cond);
   CheckClass(W.Cond,TJSPrimaryExpressionIdent);
-  AssertEquals('a',TJSPrimaryExpressionIdent(W.Cond).Name);
+  AssertEquals('Name 1','a',TJSPrimaryExpressionIdent(W.Cond).Name);
   AssertNotNull('Statement condition assigned',W.body);
   CheckClass(W.Body,TJSExpressionStatement);
   AssertNotNull('Expression statement expression',TJSExpressionStatement(W.Body).A);
   CheckClass(TJSExpressionStatement(W.Body).A,TJSPrimaryExpressionIdent);
-  AssertEquals('b',TJSPrimaryExpressionIdent(TJSExpressionStatement(W.Body).A).Name);
+  AssertEquals('Name 1','b',TJSPrimaryExpressionIdent(TJSExpressionStatement(W.Body).A).Name);
 end;
 
 procedure TTestJSParser.TestForEmpty;
@@ -2335,7 +2383,7 @@ begin
   CheckClass(F.Body,TJSExpressionStatement);
   AssertNotNull('Expression statement expression',TJSExpressionStatement(F.Body).A);
   CheckClass(TJSExpressionStatement(F.Body).A,TJSPrimaryExpressionIdent);
-  AssertEquals('a',TJSPrimaryExpressionIdent(TJSExpressionStatement(F.Body).A).Name);
+  AssertEquals('Name 1','a',TJSPrimaryExpressionIdent(TJSExpressionStatement(F.Body).A).Name);
 end;
 
 procedure TTestJSParser.TestForEmptyBody;
@@ -2355,7 +2403,7 @@ begin
   CheckClass(F.Body,TJSExpressionStatement);
   AssertNotNull('Expression statement expression',TJSExpressionStatement(F.Body).A);
   CheckClass(TJSExpressionStatement(F.Body).A,TJSPrimaryExpressionIdent);
-  AssertEquals('a',TJSPrimaryExpressionIdent(TJSExpressionStatement(F.Body).A).Name);
+  AssertEquals('Name 1','a',TJSPrimaryExpressionIdent(TJSExpressionStatement(F.Body).A).Name);
 end;
 
 procedure TTestJSParser.TestForSimpleBody;
@@ -2374,17 +2422,17 @@ begin
   AssertNotNull('Statement step not assigned',F.Incr);
   CheckClass(F.Init,TJSPrimaryExpressionIdent);
   AssertNotNull('Expression statement expression',TJSPrimaryExpressionIdent(F.Init));
-  AssertEquals('a',TJSPrimaryExpressionIdent(F.Init).Name);
+  AssertEquals('Name 1','a',TJSPrimaryExpressionIdent(F.Init).Name);
   CheckClass(F.Incr,TJSPrimaryExpressionIdent);
   AssertNotNull('Expression statement expression',TJSPrimaryExpressionIdent(F.Incr));
-  AssertEquals('c',TJSPrimaryExpressionIdent(F.Incr).Name);
+  AssertEquals('Name 2','c',TJSPrimaryExpressionIdent(F.Incr).Name);
   CheckClass(F.Cond,TJSPrimaryExpressionIdent);
   AssertNotNull('Expression statement expression',TJSPrimaryExpressionIdent(F.Cond));
-  AssertEquals('b',TJSPrimaryExpressionIdent(F.cond).Name);
+  AssertEquals('Name 3','b',TJSPrimaryExpressionIdent(F.cond).Name);
   CheckClass(F.Body,TJSExpressionStatement);
   AssertNotNull('Expression statement expression',TJSExpressionStatement(F.Body).A);
   CheckClass(TJSExpressionStatement(F.Body).A,TJSPrimaryExpressionIdent);
-  AssertEquals('d',TJSPrimaryExpressionIdent(TJSExpressionStatement(F.Body).A).Name);
+  AssertEquals('Name 4','d',TJSPrimaryExpressionIdent(TJSExpressionStatement(F.Body).A).Name);
 end;
 
 procedure TTestJSParser.TestTryCatch;
@@ -2401,12 +2449,12 @@ begin
   CheckClass(T.Block,TJSExpressionStatement);
   AssertNotNull('Expression statement expression',TJSExpressionStatement(T.Block).A);
   CheckClass(TJSExpressionStatement(T.Block).A,TJSPrimaryExpressionIdent);
-  AssertEquals('a',TJSPrimaryExpressionIdent(TJSExpressionStatement(T.Block).A).Name);
+  AssertEquals('Name 1','a',TJSPrimaryExpressionIdent(TJSExpressionStatement(T.Block).A).Name);
   CheckClass(T.BCatch,TJSExpressionStatement);
   AssertEquals('Except object identifier name','e',T.Ident);
   AssertNotNull('Expression statement expression',TJSExpressionStatement(T.BCatch).A);
   CheckClass(TJSExpressionStatement(T.BCatch).A,TJSPrimaryExpressionIdent);
-  AssertEquals('b',TJSPrimaryExpressionIdent(TJSExpressionStatement(T.BCatch).A).Name);
+  AssertEquals('Name 2','b',TJSPrimaryExpressionIdent(TJSExpressionStatement(T.BCatch).A).Name);
   AssertNull('No Finally expression',T.BFinally);
 end;
 
@@ -2424,17 +2472,17 @@ begin
   CheckClass(T.Block,TJSExpressionStatement);
   AssertNotNull('Expression statement expression',TJSExpressionStatement(T.Block).A);
   CheckClass(TJSExpressionStatement(T.Block).A,TJSPrimaryExpressionIdent);
-  AssertEquals('a',TJSPrimaryExpressionIdent(TJSExpressionStatement(T.Block).A).Name);
+  AssertEquals('Name 1','a',TJSPrimaryExpressionIdent(TJSExpressionStatement(T.Block).A).Name);
   AssertEquals('Except object identifier name','e',T.Ident);
   CheckClass(T.BCatch,TJSExpressionStatement);
   AssertNotNull('Expression statement expression',TJSExpressionStatement(T.BCatch).A);
   CheckClass(TJSExpressionStatement(T.BCatch).A,TJSPrimaryExpressionIdent);
-  AssertEquals('b',TJSPrimaryExpressionIdent(TJSExpressionStatement(T.BCatch).A).Name);
+  AssertEquals('Name 2','b',TJSPrimaryExpressionIdent(TJSExpressionStatement(T.BCatch).A).Name);
   AssertNotNull('Finally expression',T.BFinally);
   CheckClass(T.BFinally,TJSExpressionStatement);
   AssertNotNull('Expression statement expression',TJSExpressionStatement(T.BFinally).A);
   CheckClass(TJSExpressionStatement(T.BFinally).A,TJSPrimaryExpressionIdent);
-  AssertEquals('c',TJSPrimaryExpressionIdent(TJSExpressionStatement(T.BFinally).A).Name);
+  AssertEquals('Name 3','c',TJSPrimaryExpressionIdent(TJSExpressionStatement(T.BFinally).A).Name);
 end;
 
 procedure TTestJSParser.TestTryFinally;
@@ -2451,12 +2499,12 @@ begin
   CheckClass(T.Block,TJSExpressionStatement);
   AssertNotNull('Expression statement expression',TJSExpressionStatement(T.Block).A);
   CheckClass(TJSExpressionStatement(T.Block).A,TJSPrimaryExpressionIdent);
-  AssertEquals('a',TJSPrimaryExpressionIdent(TJSExpressionStatement(T.Block).A).Name);
+  AssertEquals('Name 1','a',TJSPrimaryExpressionIdent(TJSExpressionStatement(T.Block).A).Name);
   AssertNull('No catch',T.BCatch);
   AssertNotNull('Finally expression',T.BFinally);
   AssertNotNull('Finally expression',TJSExpressionStatement(T.BFinally).A);
   CheckClass(TJSExpressionStatement(T.BFinally).A,TJSPrimaryExpressionIdent);
-  AssertEquals('c',TJSPrimaryExpressionIdent(TJSExpressionStatement(T.BFinally).A).Name);
+  AssertEquals('Name 2','c',TJSPrimaryExpressionIdent(TJSExpressionStatement(T.BFinally).A).Name);
 end;
 
 procedure TTestJSParser.TestThrow;
@@ -3013,8 +3061,8 @@ begin
   CheckClass(Exp.Declaration,TJSVariableStatement);
   V:=TJSVariableStatement(Exp.Declaration);
   AssertEquals('var type',vtVar,V.varType);
-  CheckClass(V.A,TJSVarDeclaration);
-  AssertEquals('Variable name','a',TJSVarDeclaration(V.a).Name);
+  CheckClass(V.VarDecl,TJSVarDeclaration);
+  AssertEquals('Variable name','a',TJSVarDeclaration(V.VarDecl).Name);
 end;
 
 procedure TTestJSParser.TestExportLet;
@@ -3034,8 +3082,8 @@ begin
   CheckClass(Exp.Declaration,TJSVariableStatement);
   V:=TJSVariableStatement(Exp.Declaration);
   AssertEquals('var type',vtLet,V.varType);
-  CheckClass(V.A,TJSVarDeclaration);
-  AssertEquals('Variable name','a',TJSVarDeclaration(V.a).Name);
+  CheckClass(V.VarDecl,TJSVarDeclaration);
+  AssertEquals('Variable name','a',TJSVarDeclaration(V.VarDecl).Name);
 end;
 
 procedure TTestJSParser.TestExportConst;
@@ -3054,8 +3102,8 @@ begin
   CheckClass(Exp.Declaration,TJSVariableStatement);
   V:=TJSVariableStatement(Exp.Declaration);
   AssertEquals('var type',vtConst,V.varType);
-  CheckClass(V.A,TJSVarDeclaration);
-  AssertEquals('Variable name','a',TJSVarDeclaration(V.a).Name);
+  CheckClass(V.VarDecl,TJSVarDeclaration);
+  AssertEquals('Variable name','a',TJSVarDeclaration(V.VarDecl).Name);
 end;
 
 procedure TTestJSParser.TestExportFunction;
@@ -3141,21 +3189,26 @@ procedure TTestJSParser.TestClass;
 begin
   CreateParser('class Rectangle {  } ',MinClassVersion);
   AssertEquals('class name correct','Rectangle',GetFirstClass.Name);
-  AssertEquals('class extends name correct','',GetFirstClass.Extends);
+  AssertNull('class extends name correct',GetFirstClass.Extends);
 end;
 
 procedure TTestJSParser.TestClassExtends;
+
+Var
+  Ext: TJSTypeReference;
+
 begin
   CreateParser('class Rectangle extends Shape {  function myMethod () { return null; } } ',MinClassVersion);
   AssertEquals('class name correct','Rectangle',GetFirstClass.Name);
-  AssertEquals('class extends name correct','Shape',GetFirstClass.Extends);
+  Ext:=TJSTypeReference(CheckClass('Extends is type ref',TJSTypeReference,GetFirstClass.Extends));
+  AssertEquals('class extends name correct','Shape',Ext.Name);
 end;
 
 procedure TTestJSParser.TestClassWithMethod;
 begin
   CreateParser('class Rectangle {  function myMethod () { return null; } } ',MinClassVersion);
   AssertEquals('class name correct','Rectangle',GetFirstClass.Name);
-  AssertEquals('class extends name correct','',GetFirstClass.Extends);
+  AssertNull('class extends name correct',GetFirstClass.Extends);
   AssertNotNull('Have members',GetFirstClass.Members);
   AssertEquals('Have functions',1,GetFirstClass.Members.Functions.Count);
 end;
@@ -3166,7 +3219,7 @@ Var
   X : TJSExpressionStatement;
   A  : TJSSimpleAssignStatement;
 begin
-  CreateParser('a = class Rectangle {  } ');
+  CreateParser('a = class Rectangle {  } ',MinClassVersion);
   X:=GetExpressionStatement;
   CheckClass(X.A,TJSSimpleAssignStatement);
   A:=TJSSimpleAssignStatement(X.A);
@@ -3184,8 +3237,8 @@ begin
   CreateParser('let a = class Rectangle {  } ',MinClassVersion);
   AssertEquals('class name correct',TJSVariableStatement, GetFirstStatement.ClassType);
   aVars:=TJSVariableStatement(GetFirstStatement);
-  AssertEquals('First class node is var declaration',TJSVarDeclaration,aVars.A.ClassType);
-  aVarDecl:=TJSVarDeclaration(aVars.A);
+  AssertEquals('First class node is var declaration',TJSVarDeclaration,aVars.VarDecl.ClassType);
+  aVarDecl:=TJSVarDeclaration(aVars.VarDecl);
   AssertNotNull('Var declaration has init',aVarDecl.Init);
   AssertEquals('Init is class declaration',TJSClassDeclaration,aVarDecl.Init.ClassType);
   aClass:=TJSClassDeclaration(aVarDecl.Init);
@@ -3242,17 +3295,17 @@ begin
   I:=TJSIfStatement(E);
   AssertNotNull('Statement condition assigned',I.Cond);
   CheckClass(I.Cond,TJSPrimaryExpressionIdent);
-  AssertEquals('a',TJSPrimaryExpressionIdent(I.Cond).Name);
+  AssertEquals('Name 1','a',TJSPrimaryExpressionIdent(I.Cond).Name);
   AssertNotNull('Statement condition assigned',I.Btrue);
   CheckClass(I.Btrue,TJSExpressionStatement);
   AssertNotNull('Expression statement expression',TJSExpressionStatement(I.BTrue).A);
   CheckClass(TJSExpressionStatement(I.BTrue).A,TJSPrimaryExpressionIdent);
-  AssertEquals('b',TJSPrimaryExpressionIdent(TJSExpressionStatement(I.Btrue).A).Name);
+  AssertEquals('Name 1','b',TJSPrimaryExpressionIdent(TJSExpressionStatement(I.Btrue).A).Name);
   AssertNotNull('Else Statement condition assigned',I.BFalse);
   CheckClass(I.BFalse,TJSExpressionStatement);
   AssertNotNull('Else statement expression',TJSExpressionStatement(I.BFalse).A);
   CheckClass(TJSExpressionStatement(I.BFalse).A,TJSPrimaryExpressionIdent);
-  AssertEquals('c',TJSPrimaryExpressionIdent(TJSExpressionStatement(I.BFalse).A).Name);
+  AssertEquals('Name 1','c',TJSPrimaryExpressionIdent(TJSExpressionStatement(I.BFalse).A).Name);
 end;
 
 

+ 6 - 0
packages/fcl-js/tests/tcscanner.pp

@@ -102,6 +102,7 @@ type
     procedure TestURShift;
     procedure TestURShiftEq;
     procedure TestArrow;
+    procedure TestEllipsis;
     procedure TestAwaitECMA5;
     procedure TestAwaitECMA2021;
     procedure TestBreak;
@@ -491,6 +492,11 @@ begin
   CheckToken(tjsArrow,'=>');
 end;
 
+procedure TTestJSScanner.TestEllipsis;
+begin
+  CheckToken(tjsEllipsis,'...');
+end;
+
 procedure TTestJSScanner.TestAwaitECMA5;
 begin
   CheckToken(tjsIdentifier,'await');

Файловите разлики са ограничени, защото са твърде много
+ 576 - 73
packages/fcl-js/tests/tctsparser.pp


+ 52 - 0
packages/fcl-js/tests/tctstopas.pp

@@ -32,6 +32,13 @@ Type
     Procedure Test3VarDeclarations;
     Procedure TestKeywordVarDeclaration;
     Procedure TestSimpleType;
+    Procedure TestAliasType;
+    Procedure TestAliasAliasedType;
+    Procedure TestUnionType;
+    Procedure TestIntersectionType;
+    Procedure TestUnionIntersectionType;
+    Procedure TestEnumType;
+    Procedure TestExportInterface;
   end;
 
 implementation
@@ -106,7 +113,9 @@ Var
 
 begin
   Src:=FConverter.Source;
+  Writeln('>>>');
   Writeln(Src.Text);
+  Writeln('<<<');
   I:=0;
   While (I<Src.Count) and (Trim(Src[i])='') do
     Inc(I);
@@ -160,6 +169,49 @@ begin
   CheckDeclarations('type',['TMyType = string;']);
 end;
 
+procedure TTestTSToPas.TestAliasType;
+begin
+  Convert('declare type MyType = SomeOtherType;');
+  CheckDeclarations('type',['TMyType = SomeOtherType;']);
+end;
+
+procedure TTestTSToPas.TestAliasAliasedType;
+begin
+  Converter.TypeAliases.Add('SomeOtherType=TMyOther');
+  Convert('declare type MyType = SomeOtherType;');
+  CheckDeclarations('type',['TMyType = TMyOther;']);
+end;
+
+procedure TTestTSToPas.TestUnionType;
+begin
+  Convert('declare type MyType = string | number;');
+  CheckDeclarations('type',['TMyType = JSValue; // string | number']);
+end;
+
+procedure TTestTSToPas.TestIntersectionType;
+begin
+  Convert('declare type MyType = string & number;');
+  CheckDeclarations('type',['TMyType = JSValue; // string & number']);
+end;
+
+procedure TTestTSToPas.TestUnionIntersectionType;
+begin
+  Convert('declare type MyType = number | (string & number) ;');
+  CheckDeclarations('type',['TMyType = JSValue; // number | (string & number)']);
+end;
+
+procedure TTestTSToPas.TestEnumType;
+begin
+  Convert('declare enum Color {Red, Green, Blue} ;');
+  CheckDeclarations('type',['TColor = (Red, Green, Blue);']);
+end;
+
+procedure TTestTSToPas.TestExportInterface;
+begin
+//  Convert('export interface Color { function get() : string; } ;');
+//  CheckDeclarations('type',['TColor = (Red, Green, Blue);']);
+end;
+
 Initialization
   Registertest(TTestTSToPas);
 end.

+ 10 - 10
packages/fcl-js/tests/tcwriter.pp

@@ -1000,7 +1000,7 @@ Var
 begin
   S:=TJSVariableStatement.Create(0,0);
   V:=TJSVarDeclaration.Create(0,0);
-  S.A:=V;
+  S.VarDecl:=V;
   V.Name:='a';
   AssertWrite('simple var','var a',S);
 end;
@@ -1013,7 +1013,7 @@ begin
   S:=TJSVariableStatement.Create(0,0);
   S.varType:=vtLet;
   V:=TJSVarDeclaration.Create(0,0);
-  S.A:=V;
+  S.VarDecl:=V;
   V.Name:='a';
   AssertWrite('simple let','let a',S);
 end;
@@ -1026,7 +1026,7 @@ begin
   S:=TJSVariableStatement.Create(0,0);
   S.varType:=vtConst;
   V:=TJSVarDeclaration.Create(0,0);
-  S.A:=V;
+  S.VarDecl:=V;
   V.Name:='a';
   V.Init:=CreateLiteral(1);
   AssertWrite('simple const','const a = 1',S);
@@ -1053,7 +1053,7 @@ begin
   L:=TJSVariableDeclarationList.Create(0,0);
   V:=TJSVarDeclaration.Create(0,0);
   L.A:=V;
-  S.A:=L;
+  S.VarDecl:=L;
   V.Name:='a';
   AssertWrite('simple var','var a',S);
 end;
@@ -1067,7 +1067,7 @@ Var
 begin
   S:=TJSVariableStatement.Create(0,0);
   L:=TJSVariableDeclarationList.Create(0,0);
-  S.A:=L;
+  S.VarDecl:=L;
   V:=TJSVarDeclaration.Create(0,0);
   L.A:=V;
   V.Name:='a';
@@ -1088,7 +1088,7 @@ Var
 begin
   S:=TJSVariableStatement.Create(0,0);
   L:=TJSVariableDeclarationList.Create(0,0);
-  S.A:=L;
+  S.VarDecl:=L;
   V:=TJSVarDeclaration.Create(0,0);
   L.A:=V;
   V.Name:='a';
@@ -1429,7 +1429,7 @@ var
     V:=TJSVariableStatement.Create(0,0);
     Result.Init:=V;
     C:=TJSCommaExpression.Create(0,0);
-    V.A:=C;
+    V.VarDecl:=C;
     C.A:=CreateAssignSt(CreateIdent(LoopVar),StartExpr);
     C.B:=CreateAssignSt(CreateIdent(LoopEndVar),EndExpr);
 
@@ -2435,7 +2435,7 @@ begin
   Exp:=TJSExportStatement.Create(0,0);
   VS:=TJSVariableStatement.Create(0,0);
   VV:=TJSVarDeclaration.Create(0,0);
-  VS.A:=VV;
+  VS.VarDecl:=VV;
   VV.Name:='a';
   Exp.Declaration:=VS;
   AssertWrite('Export statement','export var a',Exp);
@@ -2451,7 +2451,7 @@ begin
   VS:=TJSVariableStatement.Create(0,0);
   VS.varType:=vtLet;
   VV:=TJSVarDeclaration.Create(0,0);
-  VS.A:=VV;
+  VS.VarDecl:=VV;
   VV.varType:=vtLet;
   VV.Name:='a';
   Exp.Declaration:=VS;
@@ -2468,7 +2468,7 @@ begin
   VS:=TJSVariableStatement.Create(0,0);
   VS.varType:=vtConst;
   VV:=TJSVarDeclaration.Create(0,0);
-  VS.A:=VV;
+  VS.VarDecl:=VV;
   VV.varType:=vtConst;
   VV.Name:='a';
   Exp.Declaration:=VS;

+ 2 - 2
packages/fcl-js/tests/testjs.lpi

@@ -20,13 +20,13 @@
     </PublishOptions>
     <RunParams>
       <local>
-        <CommandLineParams Value="--suite=TTestTSToPas.TestSimpleType"/>
+        <CommandLineParams Value="--suite=TTestTypeScriptParser.TestDeclareTypeArrowFunctionShort"/>
       </local>
       <FormatVersion Value="2"/>
       <Modes Count="1">
         <Mode0 Name="default">
           <local>
-            <CommandLineParams Value="--suite=TTestTSToPas.TestSimpleType"/>
+            <CommandLineParams Value="--suite=TTestTypeScriptParser.TestDeclareTypeArrowFunctionShort"/>
           </local>
         </Mode0>
       </Modes>

Някои файлове не бяха показани, защото твърде много файлове са промени