浏览代码

* const and let statements

Michaël Van Canneyt 3 年之前
父节点
当前提交
652643499a

+ 22 - 11
packages/fcl-js/src/jsparser.pp

@@ -25,6 +25,7 @@ uses
 Const
 Const
    SEmptyLabel = '';
    SEmptyLabel = '';
    MinAsyncVersion = ecma2021;
    MinAsyncVersion = ecma2021;
+   minLetVersion = ecma2021;
 
 
 Type
 Type
   TECMAVersion = jsScanner.TECMAVersion;
   TECMAVersion = jsScanner.TECMAVersion;
@@ -101,9 +102,9 @@ Type
     function ParseThrowStatement: TJSElement;
     function ParseThrowStatement: TJSElement;
     function ParseTryStatement: TJSElement;
     function ParseTryStatement: TJSElement;
     function ParseUnaryExpression: TJSElement;
     function ParseUnaryExpression: TJSElement;
-    function ParseVariableDeclaration: TJSElement;
-    function ParseVariableDeclarationList: TJSElement;
-    function ParseVariableStatement: TJSElement;
+    function ParseVariableDeclaration(aVarType : TJSVarType = vtVar): TJSElement;
+    function ParseVariableDeclarationList(aVarType : TJSVarType = vtVar): TJSElement;
+    function ParseVariableStatement(aVarType : TJSVarType = vtVar): TJSElement;
     function ParseWithStatement: TJSElement;
     function ParseWithStatement: TJSElement;
   Protected
   Protected
     Procedure CheckParser;
     Procedure CheckParser;
@@ -1334,16 +1335,17 @@ begin
   {$ifdef debugparser}  Writeln('Exit ParseAssignmentExpression');{$endif debugparser}
   {$ifdef debugparser}  Writeln('Exit ParseAssignmentExpression');{$endif debugparser}
 end;
 end;
 
 
-function TJSParser.ParseVariableDeclaration: TJSElement;
+function TJSParser.ParseVariableDeclaration(aVarType : TJSVarType = vtVar): TJSElement;
 
 
 Var
 Var
   V : TJSVarDeclaration;
   V : TJSVarDeclaration;
 
 
 begin
 begin
   {$ifdef debugparser}  Writeln('ParseVariableDeclaration');{$endif debugparser}
   {$ifdef debugparser}  Writeln('ParseVariableDeclaration');{$endif debugparser}
-  V:=TJSVarDeclaration(CreateElement(TJSVarDeclaration));;
+  V:=TJSVarDeclaration(CreateElement(TJSVarDeclaration));
   try
   try
     V.Name:=CurrenttokenString;
     V.Name:=CurrenttokenString;
+    V.VarType:=aVarType;
     Consume(tjsIdentifier);
     Consume(tjsIdentifier);
     if (CurrentToken=tjsAssign) then
     if (CurrentToken=tjsAssign) then
       begin
       begin
@@ -1359,7 +1361,7 @@ begin
   {$ifdef debugparser}  Writeln('Exit ParseVariableDeclaration');{$endif debugparser}
   {$ifdef debugparser}  Writeln('Exit ParseVariableDeclaration');{$endif debugparser}
 end;
 end;
 
 
-function TJSParser.ParseVariableDeclarationList: TJSElement;
+function TJSParser.ParseVariableDeclarationList(aVarType : TJSVarType = vtVar): TJSElement;
 
 
 Var
 Var
   E,N : TJSElement;
   E,N : TJSElement;
@@ -1367,7 +1369,7 @@ Var
 
 
 begin
 begin
   {$ifdef debugparser}  Writeln('ParseVariableDeclarationList entry');{$endif debugparser}
   {$ifdef debugparser}  Writeln('ParseVariableDeclarationList entry');{$endif debugparser}
-  E:=ParseVariableDeclaration;
+  E:=ParseVariableDeclaration(aVarType);
   If (CurrentToken<>tjsComma) then
   If (CurrentToken<>tjsComma) then
     Result:=E
     Result:=E
   else
   else
@@ -1387,7 +1389,10 @@ begin
   {$ifdef debugparser}  Writeln('ParseVariableDeclarationList exit');{$endif debugparser}
   {$ifdef debugparser}  Writeln('ParseVariableDeclarationList exit');{$endif debugparser}
 end;
 end;
 
 
-function TJSParser.ParseVariableStatement : TJSElement;
+function TJSParser.ParseVariableStatement(aVarType : TJSVarType = vtVar): TJSElement;
+
+Const
+   InitialTokens : Array[TJSVarType] of TJSToken = (tjsVar,tjsLet,tjsConst);
 
 
 Var
 Var
   V : TJSVariableStatement;
   V : TJSVariableStatement;
@@ -1395,11 +1400,12 @@ Var
 begin
 begin
   {$ifdef debugparser}  Writeln('ParseVariableStatement entry');{$endif debugparser}
   {$ifdef debugparser}  Writeln('ParseVariableStatement entry');{$endif debugparser}
   Result:=Nil;
   Result:=Nil;
-  Consume(tjsVar);
-  Result:=ParseVariableDeclarationList;
+  Consume(InitialTokens[aVarType]);
+  Result:=ParseVariableDeclarationList(aVarType);
   try
   try
     Consume(tjsSemicolon,true);
     Consume(tjsSemicolon,true);
     V:=TJSVariableStatement(CreateElement(TJSVariableStatement));
     V:=TJSVariableStatement(CreateElement(TJSVariableStatement));
+    V.varType:=aVarType;
     V.A:=Result;
     V.A:=Result;
     Result:=V;
     Result:=V;
   except
   except
@@ -1988,8 +1994,12 @@ begin
   Case CurrentToken of
   Case CurrentToken of
     tjsCurlyBraceOpen :
     tjsCurlyBraceOpen :
       Result:=ParseBlock;
       Result:=ParseBlock;
+    tjsLet:
+      Result:=ParseVariableStatement(vtLet);
+    tjsConst:
+      Result:=ParseVariableStatement(vtConst);
     tjsVar:
     tjsVar:
-      Result:=ParseVariableStatement;
+      Result:=ParseVariableStatement(vtVar);
     tjsSemicolon:
     tjsSemicolon:
       Result:=ParseEmptyStatement;
       Result:=ParseEmptyStatement;
     tjsIf:
     tjsIf:
@@ -2033,6 +2043,7 @@ Const
   StatementTokens = [tjsNULL, tjsTRUE, tjsFALSE,
   StatementTokens = [tjsNULL, tjsTRUE, tjsFALSE,
       tjsTHIS, tjsIdentifier,jstoken.tjsSTRING,tjsNUMBER,
       tjsTHIS, tjsIdentifier,jstoken.tjsSTRING,tjsNUMBER,
       tjsBraceOpen,tjsCurlyBraceOpen,tjsSquaredBraceOpen,
       tjsBraceOpen,tjsCurlyBraceOpen,tjsSquaredBraceOpen,
+      tjsLet, tjsConst,
       tjsNew,tjsDelete,tjsVoid,tjsTypeOf,
       tjsNew,tjsDelete,tjsVoid,tjsTypeOf,
       tjsPlusPlus,tjsMinusMinus,
       tjsPlusPlus,tjsMinusMinus,
       tjsPlus,tjsMinus,tjsNot,tjsNE,tjsSNE,tjsSemicolon,
       tjsPlus,tjsMinus,tjsNot,tjsNE,tjsSNE,tjsSemicolon,

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

@@ -29,7 +29,7 @@ Const
   // Here we specify what keywords are not keywords for 'older' versions.
   // Here we specify what keywords are not keywords for 'older' versions.
   NonJSKeywords : Array [TECMAVersion] of TJSTokens
   NonJSKeywords : Array [TECMAVersion] of TJSTokens
     = (
     = (
-        [tjsAwait, tjsClass, tjsConst,tjsDebugger,tjsEnum, tjsExport, tjsExtends, tjsImport, tjsSUPER, tjsYield], //ecma5
+        [tjsAwait, tjsClass, tjsConst,tjsDebugger,tjsEnum, tjsExport, tjsExtends, tjsImport, tjsLet, tjsSUPER, tjsYield], //ecma5
         [] // ecma2022
         [] // ecma2022
       );
       );
 
 

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

@@ -43,6 +43,7 @@ type
      tjsELSE, tjsENUM, tjsEXPORT, tjsEXTENDS,
      tjsELSE, tjsENUM, tjsEXPORT, tjsEXTENDS,
      tjsFALSE, tjsFINALLY, tjsFOR, tjsFUNCTION,
      tjsFALSE, tjsFINALLY, tjsFOR, tjsFUNCTION,
      tjsIF, tjsIMPORT, tjsIN, tjsINSTANCEOF,
      tjsIF, tjsIMPORT, tjsIN, tjsINSTANCEOF,
+     tjsLet,
      tjsNEW, tjsNULL,
      tjsNEW, tjsNULL,
      tjsRETURN,
      tjsRETURN,
      tjsSUPER, tjsSWITCH,
      tjsSUPER, tjsSWITCH,
@@ -80,6 +81,7 @@ const
      'else','enum','export','extends',
      'else','enum','export','extends',
      'false','finally', 'for', 'function',
      'false','finally', 'for', 'function',
      'if', 'import', 'in', 'instanceof',
      'if', 'import', 'in', 'instanceof',
+     'let',
      'new','null',
      'new','null',
      'return',
      'return',
      'super', 'switch',
      'super', 'switch',

+ 11 - 2
packages/fcl-js/src/jstree.pp

@@ -349,9 +349,16 @@ Type
   end;
   end;
   TJSUnaryClass = class of TJSUnary;
   TJSUnaryClass = class of TJSUnary;
 
 
-  { TJSVariableStatement - e.g. 'var A' }
+  { TJSVariableStatement - e.g. 'var A' 'let A', 'const a'}
 
 
-  TJSVariableStatement = Class(TJSUnary);
+  TJSVarType = (vtVar,vtLet,vtConst);
+  TJSVarTypes = Set of TJSVarType;
+  TJSVariableStatement = Class(TJSUnary)
+  private
+    FVarType: TJSVarType;
+  Public
+    Property varType : TJSVarType Read FVarType Write FVarType;
+  end;
 
 
   { TJSExpressionStatement - A; }
   { TJSExpressionStatement - A; }
 
 
@@ -768,10 +775,12 @@ Type
   private
   private
     FInit: TJSElement;
     FInit: TJSElement;
     FName: String;
     FName: String;
+    FVarType: TJSVarType;
   Public
   Public
     Destructor Destroy; override;
     Destructor Destroy; override;
     Property Name : String Read FName Write FName;
     Property Name : String Read FName Write FName;
     Property Init : TJSElement Read FInit Write FInit;
     Property Init : TJSElement Read FInit Write FInit;
+    Property VarType : TJSVarType Read FVarType Write FVarType;
   end;
   end;
 
 
   { TJSIfStatement - e.g. if (Cond) btrue else bfalse }
   { TJSIfStatement - e.g. if (Cond) btrue else bfalse }

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

@@ -1910,8 +1910,11 @@ end;
 
 
 procedure TJSWriter.WriteVariableStatement(El: TJSVariableStatement);
 procedure TJSWriter.WriteVariableStatement(El: TJSVariableStatement);
 
 
+Const
+  Keywords : Array[TJSVarType] of string = ('var','let','const');
+
 begin
 begin
-  Write('var ');
+  Write(Keywords[el.varType]+' ');
   FSkipRoundBrackets:=true;
   FSkipRoundBrackets:=true;
   WriteJS(El.A);
   WriteJS(El.A);
 end;
 end;

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

@@ -23,6 +23,7 @@ type
     Procedure CreateParser(Const ASource : string; aVersion : TECMAVersion = TECMAVersion.ecma5);
     Procedure CreateParser(Const ASource : string; aVersion : TECMAVersion = TECMAVersion.ecma5);
     Procedure CheckClass(E : TJSElement; C : TJSElementClass);
     Procedure CheckClass(E : TJSElement; C : TJSElementClass);
     Procedure AssertEquals(Const AMessage : String; Expected, Actual : TJSType); overload;
     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 AssertIdentifier(Msg : String; El : TJSElement; Const AName : TJSString);
     Function  GetSourceElements : TJSSourceElements;
     Function  GetSourceElements : TJSSourceElements;
     Function  GetVars : TJSElementNodes;
     Function  GetVars : TJSElementNodes;
@@ -99,8 +100,10 @@ type
     procedure TestArrayExpressionStringArgs;
     procedure TestArrayExpressionStringArgs;
     procedure TestArrayExpressionIdentArgs;
     procedure TestArrayExpressionIdentArgs;
     Procedure TestVarDeclarationSimple;
     Procedure TestVarDeclarationSimple;
+    Procedure TestLetDeclarationSimple;
     procedure TestVarDeclarationDouble;
     procedure TestVarDeclarationDouble;
     procedure TestVarDeclarationSimpleInit;
     procedure TestVarDeclarationSimpleInit;
+    procedure TestConstDeclarationSimpleInit;
     procedure TestVarDeclarationDoubleInit;
     procedure TestVarDeclarationDoubleInit;
     procedure TestBlockEmpty;
     procedure TestBlockEmpty;
     procedure TestBlockEmptyStatement;
     procedure TestBlockEmptyStatement;
@@ -154,6 +157,17 @@ begin
   AssertEquals(AMessage,NE,NA);
   AssertEquals(AMessage,NE,NA);
 end;
 end;
 
 
+procedure TTestJSParser.AssertEquals(const AMessage: String; Expected, Actual: TJSVarType);
+
+Var
+  NE,NA : String;
+
+begin
+  NE:=GetEnumName(TypeInfo(TJSVarType),Ord(Expected));
+  NA:=GetEnumName(TypeInfo(TJSVarType),Ord(Actual));
+  AssertEquals(AMessage,NE,NA);
+end;
+
 Procedure TTestJSParser.AssertIdentifier(Msg: String; El: TJSElement;
 Procedure TTestJSParser.AssertIdentifier(Msg: String; El: TJSElement;
   Const AName: TJSString);
   Const AName: TJSString);
 
 
@@ -1563,8 +1577,24 @@ Var
 begin
 begin
   CreateParser('var a;');
   CreateParser('var a;');
   X:=GetFirstVar;
   X:=GetFirstVar;
+  AssertNotNull('Variable statement assigned',(X));
   CheckClass(X,TJSVarDeclaration);
   CheckClass(X,TJSVarDeclaration);
   V:=TJSVarDeclaration(X);
   V:=TJSVarDeclaration(X);
+  AssertEquals('correct variable type', vtVar, V.VarType);
+  AssertEquals('variable name correct registered', 'a', V.Name);
+  AssertNull('No initialization expression', V.Init);
+end;
+
+procedure TTestJSParser.TestLetDeclarationSimple;
+Var
+  X : TJSELement;
+  V : TJSVarDeclaration;
+begin
+  CreateParser('let a;',minLetVersion);
+  X:=GetFirstVar;
+  CheckClass(X,TJSVarDeclaration);
+  V:=TJSVarDeclaration(X);
+  AssertEquals('correct variable type', vtLet, V.VarType);
 //  AssertNotNull('Variable statement assigned',(X));
 //  AssertNotNull('Variable statement assigned',(X));
   AssertEquals('variable name correct registered', 'a', V.Name);
   AssertEquals('variable name correct registered', 'a', V.Name);
   AssertNull('No initialization expression', V.Init);
   AssertNull('No initialization expression', V.Init);
@@ -1582,11 +1612,13 @@ begin
   X:=GetFirstVar;
   X:=GetFirstVar;
   CheckClass(X,TJSVarDeclaration);
   CheckClass(X,TJSVarDeclaration);
   V:=TJSVarDeclaration(X);
   V:=TJSVarDeclaration(X);
+  AssertEquals('correct variable type', vtVar, V.VarType);
 //  AssertNotNull('Variable statement assigned',(X));
 //  AssertNotNull('Variable statement assigned',(X));
   AssertEquals('variable name correct registered', 'a', V.name);
   AssertEquals('variable name correct registered', 'a', V.name);
   X:=GetVars.Nodes[1].Node;
   X:=GetVars.Nodes[1].Node;
   CheckClass(X,TJSVarDeclaration);
   CheckClass(X,TJSVarDeclaration);
   V:=TJSVarDeclaration(X);
   V:=TJSVarDeclaration(X);
+  AssertEquals('correct variable type', vtVar, V.VarType);
   AssertEquals('variable name correct registered', 'b', V.Name);
   AssertEquals('variable name correct registered', 'b', V.Name);
   AssertNull('No initialization expression', V.Init);
   AssertNull('No initialization expression', V.Init);
 end;
 end;
@@ -1608,6 +1640,23 @@ begin
   AssertEquals('Member name identifier correct', 'b', TJSPrimaryExpressionIdent(V.init).Name);
   AssertEquals('Member name identifier correct', 'b', TJSPrimaryExpressionIdent(V.init).Name);
 end;
 end;
 
 
+procedure TTestJSParser.TestConstDeclarationSimpleInit;
+Var
+  X : TJSELement;
+  V : TJSVarDeclaration;
+begin
+  CreateParser('const a = 1;',MinLetVersion);
+  X:=GetFirstVar;
+  CheckClass(X,TJSVarDeclaration);
+  V:=TJSVarDeclaration(X);
+//  AssertNotNull('Variable statement assigned',(X));
+  AssertEquals('variable name correct registered', 'a', V.Name);
+  AssertNotNull('Initialization expression present', V.Init);
+  CheckClass(V.Init,TJSLiteral);
+  AssertEquals('Expression value type correct', jstNumber,TJSLiteral(V.Init).Value.ValueType);
+  AssertEquals('Expression value correct', 1.0, TJSLiteral(V.Init).Value.AsNumber);
+end;
+
 procedure TTestJSParser.TestVarDeclarationDoubleInit;
 procedure TTestJSParser.TestVarDeclarationDoubleInit;
 
 
 Var
 Var

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

@@ -131,6 +131,8 @@ type
     procedure TestImportECMA2021;
     procedure TestImportECMA2021;
     procedure TestIn;
     procedure TestIn;
     procedure TestInstanceOf;
     procedure TestInstanceOf;
+    procedure TestLetECMA5;
+    procedure TestLetECMA2021;
     procedure TestNew;
     procedure TestNew;
     procedure TestReturn;
     procedure TestReturn;
     procedure TestSuperECMA5;
     procedure TestSuperECMA5;
@@ -676,6 +678,16 @@ begin
   CheckToken(tjsinstanceof,'instanceof');
   CheckToken(tjsinstanceof,'instanceof');
 end;
 end;
 
 
+procedure TTestJSScanner.TestLetECMA5;
+begin
+  CheckToken(tjsIdentifier,'let');
+end;
+
+procedure TTestJSScanner.TestLetECMA2021;
+begin
+  CheckToken(tjsLet,'let');
+end;
+
 procedure TTestJSScanner.TestNew;
 procedure TTestJSScanner.TestNew;
 
 
 begin
 begin

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

@@ -97,6 +97,8 @@ type
     Procedure TestVarListDeclaration;
     Procedure TestVarListDeclaration;
     Procedure TestVarListDeclarationInit;
     Procedure TestVarListDeclarationInit;
     Procedure TestVarDeclarationStatement;
     Procedure TestVarDeclarationStatement;
+    Procedure TestLetDeclarationStatement;
+    Procedure TestConstDeclarationStatement;
     Procedure TestVarListDeclarationStatement;
     Procedure TestVarListDeclarationStatement;
     Procedure TestVarListDeclarationStatement2Vars;
     Procedure TestVarListDeclarationStatement2Vars;
     Procedure TestVarListDeclarationStatement3Vars;
     Procedure TestVarListDeclarationStatement3Vars;
@@ -976,6 +978,33 @@ begin
   AssertWrite('simple var','var a',S);
   AssertWrite('simple var','var a',S);
 end;
 end;
 
 
+procedure TTestStatementWriter.TestLetDeclarationStatement;
+Var
+  S : TJSVariableStatement;
+  V : TJSVarDeclaration;
+begin
+  S:=TJSVariableStatement.Create(0,0);
+  S.varType:=vtLet;
+  V:=TJSVarDeclaration.Create(0,0);
+  S.A:=V;
+  V.Name:='a';
+  AssertWrite('simple let','let a',S);
+end;
+
+procedure TTestStatementWriter.TestConstDeclarationStatement;
+Var
+  S : TJSVariableStatement;
+  V : TJSVarDeclaration;
+begin
+  S:=TJSVariableStatement.Create(0,0);
+  S.varType:=vtConst;
+  V:=TJSVarDeclaration.Create(0,0);
+  S.A:=V;
+  V.Name:='a';
+  V.Init:=CreateLiteral(1);
+  AssertWrite('simple const','const a = 1',S);
+end;
+
 procedure TTestStatementWriter.TestVarListDeclarationStatement;
 procedure TTestStatementWriter.TestVarListDeclarationStatement;
 
 
 Var
 Var