فهرست منبع

* Import statement

Michaël Van Canneyt 3 سال پیش
والد
کامیت
54415f8f4e
5فایلهای تغییر یافته به همراه613 افزوده شده و 4 حذف شده
  1. 134 4
      packages/fcl-js/src/jsparser.pp
  2. 85 0
      packages/fcl-js/src/jstree.pp
  3. 45 0
      packages/fcl-js/src/jswriter.pp
  4. 225 0
      packages/fcl-js/tests/tcparser.pp
  5. 124 0
      packages/fcl-js/tests/tcwriter.pp

+ 134 - 4
packages/fcl-js/src/jsparser.pp

@@ -25,8 +25,11 @@ uses
 Const
 Const
    SEmptyLabel = '';
    SEmptyLabel = '';
    MinAsyncVersion = ecma2021;
    MinAsyncVersion = ecma2021;
+   MinAwaitVersion = ecma2021;
+   MinYieldVersion = ecma2021;
    minLetVersion = ecma2021;
    minLetVersion = ecma2021;
    MinDebuggerVersion = ecma2021;
    MinDebuggerVersion = ecma2021;
+   MinImportVersion = ecma2021;
 
 
 Type
 Type
   TECMAVersion = jsScanner.TECMAVersion;
   TECMAVersion = jsScanner.TECMAVersion;
@@ -50,6 +53,11 @@ Type
     FLabelSets,
     FLabelSets,
     FCurrentLabelSet:TJSLabelSet;
     FCurrentLabelSet:TJSLabelSet;
     FLabels : TJSLabel;
     FLabels : TJSLabel;
+    // these check that current token is identifier with specific value:
+    // as, from, get, of, set, target
+    function  IdentifierIsLiteral(aValue : String) : Boolean;
+    Procedure CheckIdentifierLiteral(aValue : String);
+    function ConsumeIdentifierLiteral(aValue: String): TJSToken;
     function CheckSemiColonInsert(aToken: TJSToken; Consume: Boolean): Boolean;
     function CheckSemiColonInsert(aToken: TJSToken; Consume: Boolean): Boolean;
     function EnterLabel(ALabelName: String): TJSLabel;
     function EnterLabel(ALabelName: String): TJSLabel;
     procedure Expect(aToken: TJSToken);
     procedure Expect(aToken: TJSToken);
@@ -80,6 +88,7 @@ Type
     function ParseFunctionBody: TJSFunctionBody;
     function ParseFunctionBody: TJSFunctionBody;
     function ParseIdentifier: String;
     function ParseIdentifier: String;
     function ParseIfStatement: TJSElement;
     function ParseIfStatement: TJSElement;
+    function ParseImportStatement: TJSElement;
     function ParseIterationStatement: TJSElement;
     function ParseIterationStatement: TJSElement;
     function ParseLabeledStatement: TJSElement;
     function ParseLabeledStatement: TJSElement;
     function ParseLeftHandSideExpression: TJSElement;
     function ParseLeftHandSideExpression: TJSElement;
@@ -167,6 +176,8 @@ Resourcestring
   SErrInvalidnumber          = 'Invalid numerical value: %s';
   SErrInvalidnumber          = 'Invalid numerical value: %s';
   SErrInvalidRegularExpression = 'Invalid regular expression: %s';
   SErrInvalidRegularExpression = 'Invalid regular expression: %s';
   SErrFunctionNotAllowedHere = 'function keyword not allowed here';
   SErrFunctionNotAllowedHere = 'function keyword not allowed here';
+  SErrExpectedButFound       = 'Unexpected token. Expected "%s" but found "%s"';
+  SErrExpectedMulOrCurlyBrace = 'Unexpected token: Expected * or {, got: %s';
 
 
 { TJSScanner }
 { TJSScanner }
 
 
@@ -390,6 +401,23 @@ begin
       Error(SerrTokenMismatch,[CurrenttokenString,TokenInfos[aToken]]);
       Error(SerrTokenMismatch,[CurrenttokenString,TokenInfos[aToken]]);
 end;
 end;
 
 
+function TJSParser.IdentifierIsLiteral(aValue: String): Boolean;
+begin
+  Result:=(CurrentToken=tjsIdentifier) and (CurrentTokenString=aValue);
+end;
+
+procedure TJSParser.CheckIdentifierLiteral(aValue: String);
+begin
+  if Not IdentifierIsLiteral(aValue) then
+    Error(SErrExpectedButFound,[aValue,CurrentTokenString]);
+end;
+
+Function TJSParser.ConsumeIdentifierLiteral(aValue: String) : TJSToken;
+begin
+  CheckidentifierLiteral(aValue);
+  Result:=GetNextToken;
+end;
+
 function TJSParser.CheckSemiColonInsert(aToken : TJSToken; Consume : Boolean) : Boolean;
 function TJSParser.CheckSemiColonInsert(aToken : TJSToken; Consume : Boolean) : Boolean;
 
 
 begin
 begin
@@ -719,11 +747,21 @@ function TJSParser.ParsePrimaryExpression: TJSElement;
 
 
 Var
 Var
   R : TJSPrimaryExpressionIdent;
   R : TJSPrimaryExpressionIdent;
+  AYS : TJSUnaryExpression;
 
 
 begin
 begin
   {$ifdef debugparser}  Writeln('ParsePrimaryExpression');{$endif debugparser}
   {$ifdef debugparser}  Writeln('ParsePrimaryExpression');{$endif debugparser}
+  AYS:=Nil;
   Result:=Nil;
   Result:=Nil;
   try
   try
+    if (CurrentToken in [tjsYield,tjsAwait]) then
+      begin
+      if CurrentToken=tjsYield then
+        AYS:=TJSUnaryExpression(CreateElement(TJSYieldExpression))
+      else
+        AYS:=TJSUnaryExpression(CreateElement(TJSAwaitExpression));
+      GetNextToken;
+      end;
     Case CurrentToken of
     Case CurrentToken of
       tjsThis :
       tjsThis :
         begin
         begin
@@ -748,6 +786,11 @@ begin
     else
     else
       Result:=ParseLiteral;
       Result:=ParseLiteral;
     end; // Case;
     end; // Case;
+    if (AYS<>Nil) then
+      begin
+      AYS.A:=Result;
+      Result:=AYS;
+      end;
   except
   except
     FreeAndNil(Result);
     FreeAndNil(Result);
     Raise;
     Raise;
@@ -887,8 +930,16 @@ begin
        tjsBraceOpen:
        tjsBraceOpen:
          begin
          begin
          C:=TJSCallExpression(CreateElement(TJSCallExpression));
          C:=TJSCallExpression(CreateElement(TJSCallExpression));
-         C.Expr:=Result;
-         Result:=C;
+         if (Result is TJSUnaryExpression) and (TJSUnaryExpression(Result).PrefixOperatorToken in [tjsAwait,tjsYield]) then
+           begin
+           C.Expr:=TJSUnaryExpression(Result).A;
+           TJSUnaryExpression(Result).A:=C;
+           end
+         else
+           begin
+           C.Expr:=Result;
+           Result:=C;
+           end;
          C.Args:=ParseArguments;
          C.Args:=ParseArguments;
          end;
          end;
       else
       else
@@ -1590,6 +1641,81 @@ begin
   end;
   end;
 end;
 end;
 
 
+function TJSParser.ParseImportStatement: TJSElement;
+
+Var
+  Imp : TJSImportStatement;
+  aName,aAlias : String;
+  aExpectMore : Boolean;
+begin
+  aExpectMore:=True;
+  Consume(tjsImport);
+  Imp:=TJSImportStatement(CreateElement(TJSImportStatement));
+  try
+    Result:=Imp;
+    // Just module name
+    if CurrentToken = tjsString then
+      begin
+      Imp.ModuleName:=CurrentTokenString;
+      GetNextToken;
+      Exit;
+      end;
+    // ImportedDefaultBinding
+    if CurrentToken = tjsIdentifier then
+      begin
+      Imp.DefaultBinding:=CurrentTokenString;
+      aExpectMore:=GetNextToken=tjsCOMMA;
+      if aExpectMore then
+        GetNextToken;
+      end;
+    Case CurrentToken of
+      tjsMUL : // NameSpaceImport
+        begin
+        Consume(tjsMul);
+        ConsumeIdentifierLiteral('as');
+        Expect(tjsIdentifier);
+        Imp.NameSpaceImport:=CurrentTokenString;
+        Consume(tjsIdentifier);
+        end;
+      tjsCurlyBraceOpen:
+        begin
+        Consume(tjsCurlyBraceOpen);
+        Repeat
+          Expect(tjsIdentifier);
+          aName:=CurrentTokenString;
+          aAlias:='';
+          Consume(tjsIdentifier);
+          if IdentifierIsLiteral('as') then
+            begin
+            Consume(tjsIdentifier);
+            Expect(tjsIdentifier);
+            aAlias:=CurrentTokenString;
+            Consume(tjsIdentifier);
+            end;
+          With Imp.NamedImports.AddElement do
+            begin
+            Name:=aName;
+            Alias:=aAlias;
+            end;
+          if CurrentToken=tjsComma then
+            GetNextToken;
+        Until (CurrentToken=tjsCurlyBraceClose);
+        Consume(tjsCurlyBraceClose);
+        end
+    else
+      if aExpectMore then
+        Error(SErrExpectedMulOrCurlyBrace,[CurrentTokenString]);
+    end;
+    ConsumeIdentifierLiteral('from');
+    Expect(tjsString);
+    Imp.ModuleName:=CurrentTokenString;
+    Consume(tjsString);
+  except
+    FreeAndNil(Result);
+    Raise;
+  end;
+end;
+
 function TJSParser.ParseContinueStatement : TJSElement;
 function TJSParser.ParseContinueStatement : TJSElement;
 
 
 Var
 Var
@@ -2021,6 +2147,8 @@ begin
       Result:=ParseIterationStatement;
       Result:=ParseIterationStatement;
     tjsContinue:
     tjsContinue:
       Result:=ParseContinueStatement;
       Result:=ParseContinueStatement;
+    tjsImport:
+      Result:=ParseImportStatement;
     tjsBreak:
     tjsBreak:
       Result:=ParseBreakStatement;
       Result:=ParseBreakStatement;
     tjsReturn:
     tjsReturn:
@@ -2041,6 +2169,8 @@ begin
         Result:=ParseFunctionStatement;
         Result:=ParseFunctionStatement;
       Error(SErrFunctionNotAllowedHere);
       Error(SErrFunctionNotAllowedHere);
       end;
       end;
+    tjsAwait,
+    tjsYield,
     tjsIdentifier:
     tjsIdentifier:
       If (PeekNextToken=tjsColon) then
       If (PeekNextToken=tjsColon) then
         Result:=ParseLabeledStatement
         Result:=ParseLabeledStatement
@@ -2056,9 +2186,9 @@ function TJSParser.ParseSourceElements : TJSSourceElements;
 
 
 Const
 Const
   StatementTokens = [tjsNULL, tjsTRUE, tjsFALSE,
   StatementTokens = [tjsNULL, tjsTRUE, tjsFALSE,
-      tjsTHIS, tjsIdentifier,jstoken.tjsSTRING,tjsNUMBER,
+      tjsAWait, tjsTHIS, tjsIdentifier,jstoken.tjsSTRING,tjsNUMBER,
       tjsBraceOpen,tjsCurlyBraceOpen,tjsSquaredBraceOpen,
       tjsBraceOpen,tjsCurlyBraceOpen,tjsSquaredBraceOpen,
-      tjsLet, tjsConst, tjsDebugger,
+      tjsLet, tjsConst, tjsDebugger, tjsImport,
       tjsNew,tjsDelete,tjsVoid,tjsTypeOf,
       tjsNew,tjsDelete,tjsVoid,tjsTypeOf,
       tjsPlusPlus,tjsMinusMinus,
       tjsPlusPlus,tjsMinusMinus,
       tjsPlus,tjsMinus,tjsNot,tjsNE,tjsSNE,tjsSemicolon,
       tjsPlus,tjsMinus,tjsNot,tjsNE,tjsSNE,tjsSemicolon,

+ 85 - 0
packages/fcl-js/src/jstree.pp

@@ -401,6 +401,14 @@ Type
     Class function PrefixOperatorToken : tjsToken; Override;
     Class function PrefixOperatorToken : tjsToken; Override;
   end;
   end;
 
 
+  { TJSYieldExpression }
+
+  TJSYieldExpression = Class(TJSUnaryExpression)
+  Public
+    Class function PrefixOperatorToken : tjsToken; Override;
+  end;
+
+
   { TJSUnaryPrePlusPlusExpression - e.g. '++A' }
   { TJSUnaryPrePlusPlusExpression - e.g. '++A' }
 
 
   TJSUnaryPrePlusPlusExpression = Class(TJSUnaryExpression)
   TJSUnaryPrePlusPlusExpression = Class(TJSUnaryExpression)
@@ -864,6 +872,44 @@ Type
     Property List : TJSElement Read FList Write FList;
     Property List : TJSElement Read FList Write FList;
   end;
   end;
 
 
+  TJSNamedImportElement = Class(TCollectionItem)
+  private
+    FName : String;
+    FAlias : String;
+  Public
+    Property Name : String Read FName Write FName;
+    Property Alias : String Read FAlias Write FAlias;
+  end;
+
+  { TJSNamedImportElements - NamedImports property of TJSImportStatement }
+
+  TJSNamedImportElements = Class(TCollection)
+  private
+    function GetE(AIndex : Integer): TJSNamedImportElement;
+  Public
+    Function AddElement : TJSNamedImportElement;
+    Property Imports[AIndex : Integer] : TJSNamedImportElement Read GetE ;default;
+  end;
+
+  { TJSImportStatement }
+
+  TJSImportStatement = class(TJSElement)
+  Private
+    FDefaultBinding: String;
+    FModuleName: String;
+    FNamedImports : TJSNamedImportElements;
+    FNameSpaceImport: String;
+    function GetHaveNamedImports: Boolean;
+    function GetNamedImports: TJSNamedImportElements;
+  Public
+    Destructor Destroy; override;
+    Property ModuleName : String Read FModuleName Write FModuleName;
+    Property DefaultBinding : String Read FDefaultBinding Write FDefaultBinding;
+    Property NameSpaceImport : String Read FNameSpaceImport Write FNameSpaceImport;
+    Property HaveNamedImports : Boolean Read GetHaveNamedImports;
+    Property NamedImports : TJSNamedImportElements Read GetNamedImports;
+  end;
+
   { TJSContinueStatement - e.g. 'continue'}
   { TJSContinueStatement - e.g. 'continue'}
 
 
   TJSContinueStatement = Class(TJSTargetStatement);
   TJSContinueStatement = Class(TJSTargetStatement);
@@ -1010,6 +1056,45 @@ Type
 
 
 implementation
 implementation
 
 
+{ TJSImportStatement }
+
+function TJSImportStatement.GetNamedImports: TJSNamedImportElements;
+begin
+  if FNamedImports=Nil then
+    FNamedImports:=TJSNamedImportElements.Create(TJSNamedImportElement);
+  Result:=FNamedImports;
+end;
+
+function TJSImportStatement.GetHaveNamedImports: Boolean;
+begin
+  Result:=Assigned(FNamedImports) and (FNamedImports.Count>0);
+end;
+
+destructor TJSImportStatement.Destroy;
+begin
+  FreeAndNil(FNamedImports);
+  inherited Destroy;
+end;
+
+{ TJSNamedImportElements }
+
+function TJSNamedImportElements.GetE(aIndex: Integer): TJSNamedImportElement;
+begin
+  Result:=TJSNamedImportElement(Items[aIndex]);
+end;
+
+function TJSNamedImportElements.AddElement: TJSNamedImportElement;
+begin
+  Result:=TJSNamedImportElement(Add);
+end;
+
+{ TJSYieldExpression }
+
+class function TJSYieldExpression.PrefixOperatorToken: tjsToken;
+begin
+  Result:=tjsYield;
+end;
+
 {$IFDEF NOCLASSES}
 {$IFDEF NOCLASSES}
 { TCollectionItem }
 { TCollectionItem }
 
 

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

@@ -206,6 +206,7 @@ Type
     Procedure WriteAssignStatement(El: TJSAssignStatement);virtual;
     Procedure WriteAssignStatement(El: TJSAssignStatement);virtual;
     Procedure WriteForInStatement(El: TJSForInStatement);virtual;
     Procedure WriteForInStatement(El: TJSForInStatement);virtual;
     Procedure WriteWhileStatement(El: TJSWhileStatement);virtual;
     Procedure WriteWhileStatement(El: TJSWhileStatement);virtual;
+    Procedure WriteImportStatement(El: TJSImportStatement);virtual;
     Procedure WriteForStatement(El: TJSForStatement);virtual;
     Procedure WriteForStatement(El: TJSForStatement);virtual;
     Procedure WriteIfStatement(El: TJSIfStatement);virtual;
     Procedure WriteIfStatement(El: TJSIfStatement);virtual;
     Procedure WriteSourceElements(El: TJSSourceElements);virtual;
     Procedure WriteSourceElements(El: TJSSourceElements);virtual;
@@ -1677,6 +1678,48 @@ begin
     end;
     end;
 end;
 end;
 
 
+procedure TJSWriter.WriteImportStatement(El: TJSImportStatement);
+
+Var
+  I : integer;
+  N : TJSNamedImportElement;
+  needFrom : Boolean;
+
+begin
+  Write('import ');
+  needFrom:=False;
+  if El.DefaultBinding<>'' then
+    begin
+    Write(El.DefaultBinding+' ');
+    if (El.NameSpaceImport<>'') or El.HaveNamedImports then
+      Write(', ');
+    needFrom:=True;
+    end;
+  if El.NameSpaceImport<>''  then
+    begin
+    Write('* as '+El.NameSpaceImport+' ');
+    needFrom:=True;
+    end;
+  if El.HaveNamedImports then
+    begin
+    needFrom:=True;
+    Write('{ ');
+    For I:=0 to EL.NamedImports.Count-1 do
+      begin
+      N:=EL.NamedImports[i];
+      if I>0 then
+        Write(', ');
+      Write(N.Name+' ');
+      if N.Alias<>'' then
+        Write('as '+N.Alias+' ');
+      end;
+    Write('} ');
+    end;
+  if NeedFrom then
+    Write('from ');
+  write('"'+El.ModuleName+'"');
+end;
+
 procedure TJSWriter.WriteSwitchStatement(El: TJSSwitchStatement);
 procedure TJSWriter.WriteSwitchStatement(El: TJSSwitchStatement);
 
 
 Var
 Var
@@ -1981,6 +2024,8 @@ begin
     WriteVarDeclaration(TJSVarDeclaration(El))
     WriteVarDeclaration(TJSVarDeclaration(El))
   else if (C=TJSIfStatement) then
   else if (C=TJSIfStatement) then
     WriteIfStatement(TJSIfStatement(El))
     WriteIfStatement(TJSIfStatement(El))
+  else if (C=TJSImportStatement) then
+    WriteImportStatement(TJSImportStatement(El))
   else if C.InheritsFrom(TJSTargetStatement) then
   else if C.InheritsFrom(TJSTargetStatement) then
     WriteTargetStatement(TJSTargetStatement(El))
     WriteTargetStatement(TJSTargetStatement(El))
   else if (C=TJSReturnStatement) then
   else if (C=TJSReturnStatement) then

+ 225 - 0
packages/fcl-js/tests/tcparser.pp

@@ -94,6 +94,7 @@ type
     procedure TestExpressionPrecedenceBracePlusMul;
     procedure TestExpressionPrecedenceBracePlusMul;
     procedure TestExpressionFunction;
     procedure TestExpressionFunction;
     procedure TestFunctionCallNoArgs;
     procedure TestFunctionCallNoArgs;
+    procedure TestAwaitFunctionCallNoArgs;
     procedure TestFunctionCallOneArg;
     procedure TestFunctionCallOneArg;
     procedure TestFunctionCallTwoArgs;
     procedure TestFunctionCallTwoArgs;
     procedure TestArrayExpressionNumericalArgs;
     procedure TestArrayExpressionNumericalArgs;
@@ -140,6 +141,16 @@ type
     procedure TestSwitchOne;
     procedure TestSwitchOne;
     procedure TestSwitchTwo;
     procedure TestSwitchTwo;
     procedure TestSwitchTwoDefault;
     procedure TestSwitchTwoDefault;
+    Procedure TestImportModule;
+    Procedure TestImportImportedDefault;
+    Procedure TestImportNamespaceImport;
+    Procedure TestImportImportedDefaultAndNamespaceImport;
+    Procedure TestImportNamedImport;
+    Procedure TestImportNamedImportAlias;
+    Procedure TestImport2NamedImports;
+    Procedure TestImport2NamedImportAlias;
+    Procedure TestImport2NamedImportsComma;
+    Procedure TestImportDefaultAndNamedImport;
   end;
   end;
 
 
 implementation
 implementation
@@ -1471,6 +1482,27 @@ begin
   AssertEquals('Function name correct','abc',TJSPrimaryExpressionIdent(C.Expr).Name);
   AssertEquals('Function name correct','abc',TJSPrimaryExpressionIdent(C.Expr).Name);
 end;
 end;
 
 
+procedure TTestJSParser.TestAwaitFunctionCallNoArgs;
+
+Var
+  X : TJSExpressionStatement;
+  W : TJSAwaitExpression;
+  C : TJSCallExpression;
+
+begin
+  CreateParser('await abc();',MinAwaitVersion);
+  X:=GetExpressionStatement;
+  CheckClass(X.A,TJSAwaitExpression);
+  W:=TJSAwaitExpression(X.A);
+  CheckClass(W.A,TJSCallExpression);
+  C:=TJSCallExpression(W.A);
+  AssertEquals('No arguments',0,C.Args.Elements.Count);
+  AssertNotNull('Call function expression',C.Expr);
+  CheckClass(C.Expr,TJSPrimaryExpressionIdent);
+  AssertEquals('Function name correct','abc',TJSPrimaryExpressionIdent(C.Expr).Name);
+end;
+
+
 procedure TTestJSParser.TestFunctionCallOneArg;
 procedure TTestJSParser.TestFunctionCallOneArg;
 
 
 Var
 Var
@@ -2413,6 +2445,199 @@ begin
   AssertSame('Default',C,S.TheDefault);
   AssertSame('Default',C,S.TheDefault);
 end;
 end;
 
 
+procedure TTestJSParser.TestImportModule;
+
+Var
+  E : TJSElement;
+  Imp : TJSImportStatement absolute E;
+
+begin
+  CreateParser('import "a.js"',MinImportVersion);
+  E:=GetFirstStatement;
+  CheckClass(E,TJSImportStatement);
+  AssertEquals('DefaultImportedName','',Imp.DefaultBinding);
+  AssertEquals('NamespaceImport','',Imp.NameSpaceImport);
+  AssertEquals('Modulename','a.js',Imp.ModuleName);
+  AssertFalse('Named imports',Imp.HaveNamedImports);
+end;
+
+procedure TTestJSParser.TestImportImportedDefault;
+
+Var
+  E : TJSElement;
+  Imp : TJSImportStatement absolute E;
+
+begin
+  CreateParser('import A from "a.js"',MinImportVersion);
+  E:=GetFirstStatement;
+  CheckClass(E,TJSImportStatement);
+  AssertEquals('DefaultImportedName','A',Imp.DefaultBinding);
+  AssertEquals('NamespaceImport','',Imp.NameSpaceImport);
+  AssertEquals('Modulename','a.js',Imp.ModuleName);
+  AssertFalse('Named imports',Imp.HaveNamedImports);
+end;
+
+procedure TTestJSParser.TestImportNamespaceImport;
+Var
+  E : TJSElement;
+  Imp : TJSImportStatement absolute E;
+
+begin
+  CreateParser('import * as A from "a.js"',MinImportVersion);
+  E:=GetFirstStatement;
+  CheckClass(E,TJSImportStatement);
+  AssertEquals('DefaultImportedName','',Imp.DefaultBinding);
+  AssertEquals('NamespaceImport','A',Imp.NameSpaceImport);
+  AssertEquals('Modulename','a.js',Imp.ModuleName);
+  AssertFalse('Named imports',Imp.HaveNamedImports);
+end;
+
+procedure TTestJSParser.TestImportImportedDefaultAndNamespaceImport;
+Var
+  E : TJSElement;
+  Imp : TJSImportStatement absolute E;
+
+begin
+  CreateParser('import A, * as B from "a.js"',MinImportVersion);
+  E:=GetFirstStatement;
+  CheckClass(E,TJSImportStatement);
+  AssertEquals('DefaultImportedName','A',Imp.DefaultBinding);
+  AssertEquals('NamespaceImport','B',Imp.NameSpaceImport);
+  AssertEquals('Modulename','a.js',Imp.ModuleName);
+  AssertFalse('Named imports',Imp.HaveNamedImports);
+end;
+
+procedure TTestJSParser.TestImportNamedImport;
+
+Var
+  E : TJSElement;
+  Imp : TJSImportStatement absolute E;
+  NamedImp : TJSNamedImportElement;
+
+begin
+  CreateParser('import {A} from "a.js"',MinImportVersion);
+  E:=GetFirstStatement;
+  CheckClass(E,TJSImportStatement);
+  AssertEquals('DefaultImportedName','',Imp.DefaultBinding);
+  AssertEquals('NamespaceImport','',Imp.NameSpaceImport);
+  AssertEquals('Modulename','a.js',Imp.ModuleName);
+  AssertTrue('Named imports',Imp.HaveNamedImports);
+  AssertEquals('Named import count',1,Imp.NamedImports.Count);
+  NamedImp:=Imp.NamedImports[0];
+  AssertEquals('Named import name','A',NamedImp.Name);
+  AssertEquals('Named import alias','',NamedImp.Alias);
+end;
+
+procedure TTestJSParser.TestImportNamedImportAlias;
+Var
+  E : TJSElement;
+  Imp : TJSImportStatement absolute E;
+  NamedImp : TJSNamedImportElement;
+
+begin
+  CreateParser('import {A as C} from "a.js"',MinImportVersion);
+  E:=GetFirstStatement;
+  CheckClass(E,TJSImportStatement);
+  AssertEquals('DefaultImportedName','',Imp.DefaultBinding);
+  AssertEquals('NamespaceImport','',Imp.NameSpaceImport);
+  AssertEquals('Modulename','a.js',Imp.ModuleName);
+  AssertTrue('Named imports',Imp.HaveNamedImports);
+  AssertEquals('Named import count',1,Imp.NamedImports.Count);
+  NamedImp:=Imp.NamedImports[0];
+  AssertEquals('Named import name','A',NamedImp.Name);
+  AssertEquals('Named import alias','C',NamedImp.Alias);
+end;
+
+procedure TTestJSParser.TestImport2NamedImports;
+
+Var
+  E : TJSElement;
+  Imp : TJSImportStatement absolute E;
+  NamedImp : TJSNamedImportElement;
+
+begin
+  CreateParser('import {A, B} from "a.js"',MinImportVersion);
+  E:=GetFirstStatement;
+  CheckClass(E,TJSImportStatement);
+  AssertEquals('DefaultImportedName','',Imp.DefaultBinding);
+  AssertEquals('NamespaceImport','',Imp.NameSpaceImport);
+  AssertEquals('Modulename','a.js',Imp.ModuleName);
+  AssertTrue('Named imports',Imp.HaveNamedImports);
+  AssertEquals('Named import count',2,Imp.NamedImports.Count);
+  NamedImp:=Imp.NamedImports[0];
+  AssertEquals('Named import name 1','A',NamedImp.Name);
+  AssertEquals('Named import alias 1','',NamedImp.Alias);
+  NamedImp:=Imp.NamedImports[1];
+  AssertEquals('Named import name 2','B',NamedImp.Name);
+  AssertEquals('Named import alias 2','',NamedImp.Alias);
+end;
+
+procedure TTestJSParser.TestImport2NamedImportAlias;
+Var
+  E : TJSElement;
+  Imp : TJSImportStatement absolute E;
+  NamedImp : TJSNamedImportElement;
+
+begin
+  CreateParser('import {A as C, B as D} from "a.js"',MinImportVersion);
+  E:=GetFirstStatement;
+  CheckClass(E,TJSImportStatement);
+  AssertEquals('DefaultImportedName','',Imp.DefaultBinding);
+  AssertEquals('NamespaceImport','',Imp.NameSpaceImport);
+  AssertEquals('Modulename','a.js',Imp.ModuleName);
+  AssertTrue('Named imports',Imp.HaveNamedImports);
+  AssertEquals('Named import count',2,Imp.NamedImports.Count);
+  NamedImp:=Imp.NamedImports[0];
+  AssertEquals('Named import name 1','A',NamedImp.Name);
+  AssertEquals('Named import alias 1','C',NamedImp.Alias);
+  NamedImp:=Imp.NamedImports[1];
+  AssertEquals('Named import name 2','B',NamedImp.Name);
+  AssertEquals('Named import alias 2','D',NamedImp.Alias);
+end;
+
+procedure TTestJSParser.TestImport2NamedImportsComma;
+Var
+  E : TJSElement;
+  Imp : TJSImportStatement absolute E;
+  NamedImp : TJSNamedImportElement;
+
+begin
+  CreateParser('import {A, B, } from "a.js"',MinImportVersion);
+  E:=GetFirstStatement;
+  CheckClass(E,TJSImportStatement);
+  AssertEquals('DefaultImportedName','',Imp.DefaultBinding);
+  AssertEquals('NamespaceImport','',Imp.NameSpaceImport);
+  AssertEquals('Modulename','a.js',Imp.ModuleName);
+  AssertTrue('Named imports',Imp.HaveNamedImports);
+  AssertEquals('Named import count',2,Imp.NamedImports.Count);
+  NamedImp:=Imp.NamedImports[0];
+  AssertEquals('Named import name 1','A',NamedImp.Name);
+  AssertEquals('Named import alias 1','',NamedImp.Alias);
+  NamedImp:=Imp.NamedImports[1];
+  AssertEquals('Named import name 2','B',NamedImp.Name);
+  AssertEquals('Named import alias 2','',NamedImp.Alias);
+end;
+
+procedure TTestJSParser.TestImportDefaultAndNamedImport;
+Var
+  E : TJSElement;
+  Imp : TJSImportStatement absolute E;
+  NamedImp : TJSNamedImportElement;
+
+begin
+  CreateParser('import D, {A} from "a.js"',MinImportVersion);
+  E:=GetFirstStatement;
+  CheckClass(E,TJSImportStatement);
+  AssertEquals('DefaultImportedName','D',Imp.DefaultBinding);
+  AssertEquals('NamespaceImport','',Imp.NameSpaceImport);
+  AssertEquals('Modulename','a.js',Imp.ModuleName);
+  AssertTrue('Named imports',Imp.HaveNamedImports);
+  AssertEquals('Named import count',1,Imp.NamedImports.Count);
+  NamedImp:=Imp.NamedImports[0];
+  AssertEquals('Named import name','A',NamedImp.Name);
+  AssertEquals('Named import alias','',NamedImp.Alias);
+end;
+
 procedure TTestJSParser.TestBreak;
 procedure TTestJSParser.TestBreak;
 Var
 Var
   E : TJSElement;
   E : TJSElement;

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

@@ -181,6 +181,14 @@ type
     Procedure TestWithCompact;
     Procedure TestWithCompact;
     Procedure TestSourceElements;
     Procedure TestSourceElements;
     Procedure TestSourceElementsCompact;
     Procedure TestSourceElementsCompact;
+    Procedure TestImportModule;
+    Procedure TestImportDefaultBinding;
+    Procedure TestImportDefaultBindingNameSpace;
+    Procedure TestImportNameSpace;
+    Procedure TestImportNamedImport;
+    Procedure TestImportNamedImportAlias;
+    Procedure TestImport2NamedImport;
+    Procedure TestImportDefaultBindingNamedImport;
   end;
   end;
 
 
   { TTestExpressionWriter }
   { TTestExpressionWriter }
@@ -198,6 +206,7 @@ type
     Procedure TestUnaryVoid;
     Procedure TestUnaryVoid;
     Procedure TestUnaryTypeOf;
     Procedure TestUnaryTypeOf;
     Procedure TestUnaryAwait;
     Procedure TestUnaryAwait;
+    Procedure TestUnaryYield;
     Procedure TestPrefixPlusPLus;
     Procedure TestPrefixPlusPLus;
     Procedure TestPrefixMinusMinus;
     Procedure TestPrefixMinusMinus;
     Procedure TestUnaryMinus;
     Procedure TestUnaryMinus;
@@ -357,6 +366,11 @@ begin
   TestUnary('await expresssion',TJSAwaitExpression,'await a');
   TestUnary('await expresssion',TJSAwaitExpression,'await a');
 end;
 end;
 
 
+procedure TTestExpressionWriter.TestUnaryYield;
+begin
+  TestUnary('await expresssion',TJSYieldExpression,'yield a');
+end;
+
 procedure TTestExpressionWriter.TestPrefixPlusPLus;
 procedure TTestExpressionWriter.TestPrefixPlusPLus;
 begin
 begin
   TestUnary('prefix ++ expresssion',TJSUnaryPrePlusPlusExpression,'++a');
   TestUnary('prefix ++ expresssion',TJSUnaryPrePlusPlusExpression,'++a');
@@ -2233,6 +2247,116 @@ begin
   AssertWrite('Statement lists compact','b=b*10; c=c*2;',T);
   AssertWrite('Statement lists compact','b=b*10; c=c*2;',T);
 end;
 end;
 
 
+procedure TTestStatementWriter.TestImportModule;
+
+Var
+  Imp : TJSImportStatement;
+
+begin
+  Imp:=TJSImportStatement.Create(0,0);
+  Imp.ModuleName:='a.js';
+  AssertWrite('Import statement','import "a.js"',Imp);
+end;
+
+procedure TTestStatementWriter.TestImportDefaultBinding;
+
+Var
+  Imp : TJSImportStatement;
+
+begin
+  Imp:=TJSImportStatement.Create(0,0);
+  Imp.DefaultBinding:='A';
+  Imp.ModuleName:='a.js';
+  AssertWrite('Import statement','import A from "a.js"',Imp);
+end;
+
+procedure TTestStatementWriter.TestImportDefaultBindingNameSpace;
+
+Var
+  Imp : TJSImportStatement;
+
+begin
+  Imp:=TJSImportStatement.Create(0,0);
+  Imp.DefaultBinding:='A';
+  Imp.NameSpaceImport:='Q';
+  Imp.ModuleName:='a.js';
+  AssertWrite('Import statement','import A , * as Q from "a.js"',Imp);
+end;
+
+procedure TTestStatementWriter.TestImportNameSpace;
+
+Var
+  Imp : TJSImportStatement;
+
+begin
+  Imp:=TJSImportStatement.Create(0,0);
+  Imp.NameSpaceImport:='Q';
+  Imp.ModuleName:='a.js';
+  AssertWrite('Import statement','import * as Q from "a.js"',Imp);
+end;
+
+procedure TTestStatementWriter.TestImportNamedImport;
+
+Var
+  Imp : TJSImportStatement;
+
+begin
+  Imp:=TJSImportStatement.Create(0,0);
+  With Imp.NamedImports.AddElement do
+    begin
+    Name:='A';
+    end;
+  Imp.ModuleName:='a.js';
+  AssertWrite('Import statement','import { A } from "a.js"',Imp);
+end;
+
+procedure TTestStatementWriter.TestImportNamedImportAlias;
+Var
+  Imp : TJSImportStatement;
+
+begin
+  Imp:=TJSImportStatement.Create(0,0);
+  With Imp.NamedImports.AddElement do
+    begin
+    Name:='A';
+    Alias:='Q';
+    end;
+  Imp.ModuleName:='a.js';
+  AssertWrite('Import statement','import { A as Q } from "a.js"',Imp);
+end;
+
+procedure TTestStatementWriter.TestImport2NamedImport;
+
+Var
+  Imp : TJSImportStatement;
+
+begin
+  Imp:=TJSImportStatement.Create(0,0);
+  With Imp.NamedImports.AddElement do
+    begin
+    Name:='A';
+    end;
+  With Imp.NamedImports.AddElement do
+    begin
+    Name:='B';
+    end;
+  Imp.ModuleName:='a.js';
+  AssertWrite('Import statement','import { A , B } from "a.js"',Imp);
+end;
+
+procedure TTestStatementWriter.TestImportDefaultBindingNamedImport;
+Var
+  Imp : TJSImportStatement;
+
+begin
+  Imp:=TJSImportStatement.Create(0,0);
+  Imp.DefaultBinding:='C';
+  With Imp.NamedImports.AddElement do
+    Name:='A';
+  Imp.ModuleName:='a.js';
+  AssertWrite('Import statement','import C , { A } from "a.js"',Imp);
+end;
+
 { ---------------------------------------------------------------------
 { ---------------------------------------------------------------------
   TTestLiteralWriter
   TTestLiteralWriter
   ---------------------------------------------------------------------}
   ---------------------------------------------------------------------}