Browse Source

* New keywords for ECMA2021

Michaël Van Canneyt 3 years ago
parent
commit
985ebf3b63
3 changed files with 182 additions and 35 deletions
  1. 35 10
      packages/fcl-js/src/jsscanner.pp
  2. 17 16
      packages/fcl-js/src/jstoken.pp
  3. 130 9
      packages/fcl-js/tests/tcscanner.pp

+ 35 - 10
packages/fcl-js/src/jsscanner.pp

@@ -21,6 +21,18 @@ interface
 
 
 uses SysUtils, Classes, jstoken;
 uses SysUtils, Classes, jstoken;
 
 
+Type
+  TECMAVersion = (ecma5,ecma2021);
+
+Const
+  // TJSToken is the maximum known set of keywords.
+  // Here we specify what keywords are not keywords for 'older' versions.
+  NonJSKeywords : Array [TECMAVersion] of TJSTokens
+    = (
+        [tjsAwait, tjsClass, tjsConst,tjsDebugger,tjsEnum, tjsExport, tjsExtends, tjsImport, tjsSUPER, tjsYield], //ecma5
+        [] // ecma2022
+      );
+
 resourcestring
 resourcestring
   SErrInvalidCharacter = 'Invalid character ''%s''';
   SErrInvalidCharacter = 'Invalid character ''%s''';
   SErrOpenString = 'string exceeds end of line';
   SErrOpenString = 'string exceeds end of line';
@@ -71,6 +83,7 @@ Type
 
 
   TJSScanner = class
   TJSScanner = class
   private
   private
+    FECMAVersion: TECMAVersion;
     FReturnComments: Boolean;
     FReturnComments: Boolean;
     FReturnWhiteSpace: Boolean;
     FReturnWhiteSpace: Boolean;
     FSourceFile: TLineReader;
     FSourceFile: TLineReader;
@@ -83,6 +96,7 @@ Type
     FWasEndOfLine : Boolean;
     FWasEndOfLine : Boolean;
     FSourceStream : TStream;
     FSourceStream : TStream;
     FOwnSourceFile : Boolean;
     FOwnSourceFile : Boolean;
+    FNonKeyWords : TJSTokens;
     function CommentDiv: TJSToken;
     function CommentDiv: TJSToken;
     function DoIdentifier : TJSToken;
     function DoIdentifier : TJSToken;
     function DoMultiLineComment: TJSToken;
     function DoMultiLineComment: TJSToken;
@@ -94,12 +108,13 @@ Type
     function GetCurColumn: Integer;
     function GetCurColumn: Integer;
     function ReadUnicodeEscape: WideChar;
     function ReadUnicodeEscape: WideChar;
     Function ReadRegex : TJSToken;
     Function ReadRegex : TJSToken;
+    procedure SetECMAVersion(AValue: TECMAVersion);
   protected
   protected
     procedure Error(const Msg: string);overload;
     procedure Error(const Msg: string);overload;
     procedure Error(const Msg: string; Args: array of Const);overload;
     procedure Error(const Msg: string; Args: array of Const);overload;
   public
   public
-    constructor Create(ALineReader: TLineReader);
-    constructor Create(AStream : TStream);
+    constructor Create(ALineReader: TLineReader; ECMAVersion : TECMAVersion = ecma5);
+    constructor Create(AStream : TStream; ECMAVersion : TECMAVersion = ecma5);
     destructor Destroy; override;
     destructor Destroy; override;
     procedure OpenFile(const AFilename: string);
     procedure OpenFile(const AFilename: string);
     Function FetchRegexprToken: TJSToken;
     Function FetchRegexprToken: TJSToken;
@@ -115,6 +130,7 @@ Type
     property CurColumn: Integer read GetCurColumn;
     property CurColumn: Integer read GetCurColumn;
     property CurToken: TJSToken read FCurToken;
     property CurToken: TJSToken read FCurToken;
     property CurTokenString: string read FCurTokenString;
     property CurTokenString: string read FCurTokenString;
+    property ECMAVersion : TECMAVersion Read FECMAVersion Write SetECMAVersion;
   end;
   end;
 
 
 
 
@@ -146,13 +162,14 @@ begin
   ReadLn(FTextFile, Result);
   ReadLn(FTextFile, Result);
 end;
 end;
 
 
-constructor TJSScanner.Create(ALineReader: TLineReader);
+constructor TJSScanner.Create(ALineReader: TLineReader; ECMAVersion: TECMAVersion);
 begin
 begin
   inherited Create;
   inherited Create;
   FSourceFile := ALineReader;
   FSourceFile := ALineReader;
+  FNonKeyWords:=NonJSKeywords[ECMAVersion];
 end;
 end;
 
 
-constructor TJSScanner.Create(AStream: TStream);
+constructor TJSScanner.Create(AStream: TStream; ECMAVersion: TECMAVersion);
 begin
 begin
   FSourceStream:=ASTream;
   FSourceStream:=ASTream;
   FOwnSourceFile:=True;
   FOwnSourceFile:=True;
@@ -371,6 +388,13 @@ begin
   Result:=tjsRegEx;
   Result:=tjsRegEx;
 end;
 end;
 
 
+procedure TJSScanner.SetECMAVersion(AValue: TECMAVersion);
+begin
+  if FECMAVersion=AValue then Exit;
+  FECMAVersion:=AValue;
+  FNonKeyWords:=NonJSKeywords[aValue];
+end;
+
 function TJSScanner.DoStringLiteral: TJSToken;
 function TJSScanner.DoStringLiteral: TJSToken;
 
 
 Var
 Var
@@ -507,12 +531,13 @@ begin
   // Check if this is a keyword or identifier
   // Check if this is a keyword or identifier
   // !!!: Optimize this!
   // !!!: Optimize this!
   for i:=FirstKeyword to Lastkeyword do
   for i:=FirstKeyword to Lastkeyword do
-    if CurTokenString=TokenInfos[i] then
-      begin
-      Result := i;
-      FCurToken := Result;
-      exit;
-      end;
+    if Not (I in FNonKeyWords) then
+      if (CurTokenString=TokenInfos[i]) then
+        begin
+        Result := i;
+        FCurToken := Result;
+        exit;
+        end;
 end;
 end;
 
 
 Function TJSScanner.FetchToken: TJSToken;
 Function TJSScanner.FetchToken: TJSToken;

+ 17 - 16
packages/fcl-js/src/jstoken.pp

@@ -38,23 +38,24 @@ type
      tjsRSHIFT, tjsRSHIFTEQ,
      tjsRSHIFT, tjsRSHIFTEQ,
      tjsSEQ, tjsSNE, tjsMULEQ,
      tjsSEQ, tjsSNE, tjsMULEQ,
      { Reserved words start here. They must be last }
      { Reserved words start here. They must be last }
-     tjsBREAK,tjsCASE, tjsCATCH, tjsCONTINUE,
-     tjsDEFAULT, tjsDELETE, tjsDO,
-     tjsELSE,
-     tjsFalse, tjsFINALLY, tjsFOR, tjsFUNCTION,
-     tjsIF, tjsIN, tjsINSTANCEOF,
-     tjsNEW,tjsNULL,
+     tjsAWAIT, tjsBREAK, tjsCASE, tjsCATCH, tjsCLASS, tjsCONST, tjsCONTINUE,
+     tjsDEBUGGER, tjsDEFAULT, tjsDELETE, tjsDO,
+     tjsELSE, tjsENUM, tjsEXPORT, tjsEXTENDS,
+     tjsFALSE, tjsFINALLY, tjsFOR, tjsFUNCTION,
+     tjsIF, tjsIMPORT, tjsIN, tjsINSTANCEOF,
+     tjsNEW, tjsNULL,
      tjsRETURN,
      tjsRETURN,
-     tjsSWITCH,
+     tjsSUPER, tjsSWITCH,
      tjsTHIS, tjsTHROW, tjsTrue, tjsTRY, tjsTYPEOF,
      tjsTHIS, tjsTHROW, tjsTrue, tjsTRY, tjsTYPEOF,
      tjsVAR, tjsVOID,
      tjsVAR, tjsVOID,
      tjsWHILE, tjsWITH,
      tjsWHILE, tjsWITH,
-     tjsAWAIT
+     tjsYield
    );
    );
+   TJSTokens = Set of TJSToken;
 
 
 const
 const
-  FirstKeyword = tjsBreak;
-  LastKeyWord = tJSWith;
+  FirstKeyword = tjsAwait;
+  LastKeyWord = tJSYield;
 
 
   TokenInfos: array[TJSToken] of string = ('unknown',
   TokenInfos: array[TJSToken] of string = ('unknown',
        // Specials
        // Specials
@@ -74,18 +75,18 @@ const
         '>>', '>>=',
         '>>', '>>=',
         '===', '!==', '*=',
         '===', '!==', '*=',
         // Identifiers last
         // Identifiers last
-        'break','case','catch', 'continue',
-     'default','delete', 'do',
-     'else',
+        'await', 'break','case','catch', 'class','const','continue',
+     'debugger','default','delete', 'do',
+     'else','enum','export','extends',
      'false','finally', 'for', 'function',
      'false','finally', 'for', 'function',
-     'if', 'in', 'instanceof',
+     'if', 'import', 'in', 'instanceof',
      'new','null',
      'new','null',
      'return',
      'return',
-     'switch',
+     'super', 'switch',
      'this', 'throw', 'true', 'try', 'typeof',
      'this', 'throw', 'true', 'try', 'typeof',
      'var', 'void',
      'var', 'void',
      'while', 'with',
      'while', 'with',
-     'await'
+     'yield'
     );
     );
 
 
 
 

+ 130 - 9
packages/fcl-js/tests/tcscanner.pp

@@ -39,14 +39,14 @@ type
     FScanner : TJSScanner;
     FScanner : TJSScanner;
     FErrorSource : String;
     FErrorSource : String;
     procedure AssertEquals(AMessage: String; AExpected, AActual : TJSToken); overload;
     procedure AssertEquals(AMessage: String; AExpected, AActual : TJSToken); overload;
-    procedure CheckToken(AToken: TJSToken; ASource: String);
-    procedure CheckTokens(ASource: String; ATokens: array of TJSToken);
+    procedure CheckToken(AToken: TJSToken; ASource: String; aVersion : TECMAVersion = ecma5);
+    procedure CheckTokens(ASource: String; ATokens: array of TJSToken; aVersion : TECMAVersion = ecma5);
     procedure DoTestFloat(F: Double);
     procedure DoTestFloat(F: Double);
     procedure DoTestFloat(F: Double; S: String);
     procedure DoTestFloat(F: Double; S: String);
     procedure DoTestString(S: String);
     procedure DoTestString(S: String);
     procedure TestErrorSource;
     procedure TestErrorSource;
   protected
   protected
-    Function CreateScanner(AInput : String) : TJSScanner;
+    Function CreateScanner(AInput : String; aVersion : TECMAVersion = ecma5) : TJSScanner;
     procedure FreeScanner;
     procedure FreeScanner;
     procedure SetUp; override;
     procedure SetUp; override;
     procedure TearDown; override;
     procedure TearDown; override;
@@ -101,22 +101,40 @@ type
     procedure TestStarEq;
     procedure TestStarEq;
     procedure TestURShift;
     procedure TestURShift;
     procedure TestURShiftEq;
     procedure TestURShiftEq;
+    procedure TestAwaitECMA5;
+    procedure TestAwaitECMA2021;
     procedure TestBreak;
     procedure TestBreak;
     procedure TestCase;
     procedure TestCase;
     procedure TestCatch;
     procedure TestCatch;
+    procedure TestClassECMA5;
+    procedure TestClassECMA2021;
+    procedure TestConstECMA5;
+    procedure TestConstECMA2021;
     procedure TestContinue;
     procedure TestContinue;
+    procedure TestDebuggerECMA5;
+    procedure TestDebuggerECMA2021;
     procedure TestDefault;
     procedure TestDefault;
     procedure TestDelete;
     procedure TestDelete;
     procedure TestDO;
     procedure TestDO;
     procedure TestElse;
     procedure TestElse;
+    procedure TestEnumECMA5;
+    procedure TestEnumECMA2021;
+    procedure TestExportECMA5;
+    procedure TestExportECMA2021;
+    procedure TestExtendsECMA5;
+    procedure TestExtendsECMA2021;
     procedure TestFinally;
     procedure TestFinally;
     procedure TestFor;
     procedure TestFor;
     procedure TestFunction;
     procedure TestFunction;
     procedure TestIf;
     procedure TestIf;
+    procedure TestImportECMA5;
+    procedure TestImportECMA2021;
     procedure TestIn;
     procedure TestIn;
     procedure TestInstanceOf;
     procedure TestInstanceOf;
     procedure TestNew;
     procedure TestNew;
     procedure TestReturn;
     procedure TestReturn;
+    procedure TestSuperECMA5;
+    procedure TestSuperECMA2021;
     procedure TestSwitch;
     procedure TestSwitch;
     procedure TestThis;
     procedure TestThis;
     procedure TestThrow;
     procedure TestThrow;
@@ -126,6 +144,8 @@ type
     procedure TestVoid;
     procedure TestVoid;
     procedure TestWhile;
     procedure TestWhile;
     procedure TestWith;
     procedure TestWith;
+    procedure TestYieldECMA5;
+    procedure TestYieldECMA2021;
     Procedure Test2Words;
     Procedure Test2Words;
     procedure Test3Words;
     procedure Test3Words;
     procedure TestIdentifier;
     procedure TestIdentifier;
@@ -147,17 +167,18 @@ type
     procedure TestFloat;
     procedure TestFloat;
     procedure TestStringError;
     procedure TestStringError;
     procedure TestFloatError;
     procedure TestFloatError;
+
   end;
   end;
 
 
 
 
 implementation
 implementation
 
 
-Function TTestJSScanner.CreateScanner(AInput : String) : TJSScanner;
+Function TTestJSScanner.CreateScanner(AInput : String; aVersion : TECMAVersion = ecma5) : TJSScanner;
 
 
 begin
 begin
   FStream:=TStringStream.Create(AInput);
   FStream:=TStringStream.Create(AInput);
   FLineReader:=TStreamLineReader.Create(Fstream);
   FLineReader:=TStreamLineReader.Create(Fstream);
-  FScanner:=TJSScanner.Create(FLineReader);
+  FScanner:=TJSScanner.Create(FLineReader,aVersion);
   Result:=FScanner;
   Result:=FScanner;
 end;
 end;
 
 
@@ -202,14 +223,14 @@ begin
     end;
     end;
 end;
 end;
 
 
-procedure TTestJSScanner.CheckToken(AToken : TJSToken; ASource : String);
+procedure TTestJSScanner.CheckToken(AToken: TJSToken; ASource: String; aVersion: TECMAVersion);
 
 
 Var
 Var
   J : TJSToken;
   J : TJSToken;
   EN2 : String;
   EN2 : String;
 
 
 begin
 begin
-  CreateScanner(ASource);
+  CreateScanner(ASource,aVersion);
   J:=Scanner.FetchToken;
   J:=Scanner.FetchToken;
   EN2:=GetEnumName(TypeINfo(TJSToken),Ord(AToken));
   EN2:=GetEnumName(TypeINfo(TJSToken),Ord(AToken));
   AssertEquals(Format('Source %s should result in %s.',[ASource,EN2]),AToken,J);
   AssertEquals(Format('Source %s should result in %s.',[ASource,EN2]),AToken,J);
@@ -461,6 +482,16 @@ begin
   CheckToken(tjsURSHIFTEQ,'>>>=');
   CheckToken(tjsURSHIFTEQ,'>>>=');
 end;
 end;
 
 
+procedure TTestJSScanner.TestAwaitECMA5;
+begin
+  CheckToken(tjsIdentifier,'await');
+end;
+
+procedure TTestJSScanner.TestAwaitECMA2021;
+begin
+  CheckToken(tjsAwait,'await',ecma2021);
+end;
+
 procedure TTestJSScanner.TestRShift;
 procedure TTestJSScanner.TestRShift;
 
 
 begin
 begin
@@ -509,12 +540,42 @@ begin
   CheckToken(tjscatch,'catch');
   CheckToken(tjscatch,'catch');
 end;
 end;
 
 
+procedure TTestJSScanner.TestClassECMA5;
+begin
+  CheckToken(tjsIdentifier,'class');
+end;
+
+procedure TTestJSScanner.TestClassECMA2021;
+begin
+  CheckToken(tjsClass,'class',ecma2021);
+end;
+
+procedure TTestJSScanner.TestConstECMA5;
+begin
+  CheckToken(tjsIdentifier,'const');
+end;
+
+procedure TTestJSScanner.TestConstECMA2021;
+begin
+  CheckToken(tjsConst,'const',ecma2021);
+end;
+
 procedure TTestJSScanner.TestContinue;
 procedure TTestJSScanner.TestContinue;
 
 
 begin
 begin
   CheckToken(tjscontinue,'continue');
   CheckToken(tjscontinue,'continue');
 end;
 end;
 
 
+procedure TTestJSScanner.TestDebuggerECMA5;
+begin
+  CheckToken(tjsidentifier,'debugger');
+end;
+
+procedure TTestJSScanner.TestDebuggerECMA2021;
+begin
+  CheckToken(tjsDebugger,'debugger',ecma2021);
+end;
+
 procedure TTestJSScanner.TestDefault;
 procedure TTestJSScanner.TestDefault;
 
 
 begin
 begin
@@ -539,6 +600,36 @@ begin
   CheckToken(tjselse,'else');
   CheckToken(tjselse,'else');
 end;
 end;
 
 
+procedure TTestJSScanner.TestEnumECMA5;
+begin
+  CheckToken(tjsIdentifier,'enum');
+end;
+
+procedure TTestJSScanner.TestEnumECMA2021;
+begin
+  CheckToken(tjsenum,'enum',ecma2021);
+end;
+
+procedure TTestJSScanner.TestExportECMA5;
+begin
+  CheckToken(tjsIdentifier,'export');
+end;
+
+procedure TTestJSScanner.TestExportECMA2021;
+begin
+  CheckToken(tjsexport,'export',ecma2021);
+end;
+
+procedure TTestJSScanner.TestExtendsECMA5;
+begin
+  CheckToken(tjsIdentifier,'extends');
+end;
+
+procedure TTestJSScanner.TestExtendsECMA2021;
+begin
+  CheckToken(tjsextends,'extends',ecma2021);
+end;
+
 procedure TTestJSScanner.TestFinally;
 procedure TTestJSScanner.TestFinally;
 
 
 begin
 begin
@@ -563,6 +654,16 @@ begin
   CheckToken(tjsif,'if');
   CheckToken(tjsif,'if');
 end;
 end;
 
 
+procedure TTestJSScanner.TestImportECMA5;
+begin
+  CheckToken(tjsIdentifier,'import');
+end;
+
+procedure TTestJSScanner.TestImportECMA2021;
+begin
+  CheckToken(tjsImport,'import',ecma2021);
+end;
+
 procedure TTestJSScanner.TestIn;
 procedure TTestJSScanner.TestIn;
 
 
 begin
 begin
@@ -587,6 +688,16 @@ begin
   CheckToken(tjsreturn,'return');
   CheckToken(tjsreturn,'return');
 end;
 end;
 
 
+procedure TTestJSScanner.TestSuperECMA5;
+begin
+  CheckToken(tjsIdentifier,'super');
+end;
+
+procedure TTestJSScanner.TestSuperECMA2021;
+begin
+  CheckToken(tjsSuper,'super',ecma2021);
+end;
+
 procedure TTestJSScanner.TestSwitch;
 procedure TTestJSScanner.TestSwitch;
 
 
 begin
 begin
@@ -641,7 +752,17 @@ begin
   CheckToken(tjswith,'with');
   CheckToken(tjswith,'with');
 end;
 end;
 
 
-procedure TTestJSScanner.CheckTokens(ASource : String; ATokens : Array of TJSToken);
+procedure TTestJSScanner.TestYieldECMA5;
+begin
+  CheckToken(tjsIdentifier,'yield');
+end;
+
+procedure TTestJSScanner.TestYieldECMA2021;
+begin
+  CheckToken(tjsYield,'yield',ecma2021);
+end;
+
+procedure TTestJSScanner.CheckTokens(ASource : String; ATokens : Array of TJSToken; aVersion: TECMAVersion = ecma5);
 
 
 Var
 Var
   I : Integer;
   I : Integer;
@@ -649,7 +770,7 @@ Var
   S : String;
   S : String;
 
 
 begin
 begin
-  CreateScanner(ASource);
+  CreateScanner(ASource,aVersion);
   For I:=Low(ATokens) to High(ATokens) do
   For I:=Low(ATokens) to High(ATokens) do
     begin
     begin
     J:=FScanner.FetchToken;
     J:=FScanner.FetchToken;