Bläddra i källkod

* Parse async functions

Michaël Van Canneyt 3 år sedan
förälder
incheckning
a214682495
3 ändrade filer med 58 tillägg och 13 borttagningar
  1. 19 3
      packages/fcl-js/src/jsparser.pp
  2. 6 6
      packages/fcl-js/src/jsscanner.pp
  3. 33 4
      packages/fcl-js/tests/tcparser.pp

+ 19 - 3
packages/fcl-js/src/jsparser.pp

@@ -24,8 +24,10 @@ uses
 
 Const
    SEmptyLabel = '';
+   MinAsyncVersion = ecma2021;
 
 Type
+  TECMAVersion = jsScanner.TECMAVersion;
 
   { TJSParser }
 
@@ -51,6 +53,7 @@ Type
     procedure Expect(aToken: TJSToken);
     procedure Consume(aToken: TJSToken; AllowSemicolonInsert : Boolean = False);
     procedure FreeCurrentLabelSet;
+    function GetVersion: TECMAVersion;
     procedure LeaveLabel;
     function LookupLabel(ALabelName: String; Kind: TJSToken): TJSLabel;
     function ParseAdditiveExpression: TJSElement;
@@ -117,7 +120,8 @@ Type
     Property NoIn : Boolean Read FNoIn Write FNoIn;
     Property IsLHS : Boolean Read FIsLHS Write FIsLHS;
   Public
-    Constructor Create(AInput: TStream);
+    Constructor Create(AInput: TStream; aVersion : TECMAVersion = ecma5);
+    // Scanner has version
     Constructor Create(AScanner : TJSScanner);
     Destructor Destroy; override;
     Function Parse : TJSElement;
@@ -127,6 +131,7 @@ Type
     Function GetNextToken : TJSToken;
     Function PeekNextToken : TJSToken;
     Function IsEndOfLine : Boolean;
+    Property ECMAVersion : TECMAVersion Read GetVersion;
   end;
 
 implementation
@@ -349,11 +354,11 @@ begin
   Error(Format(Fmt,Args));
 end;
 
-Constructor TJSParser.Create(AInput: TStream);
+Constructor TJSParser.Create(AInput: TStream; aVersion : TECMAVersion = ecma5);
 begin
   FInput:=AInput;
   FCurrent:=TJSUnknown;
-  FScanner:=TJSScanner.Create(FInput);
+  FScanner:=TJSScanner.Create(FInput,aVersion);
   FFreeScanner:=True;
 end;
 
@@ -1931,6 +1936,11 @@ begin
     end;
 end;
 
+function TJSParser.GetVersion: TECMAVersion;
+begin
+  Result:=FSCanner.ECMAVersion;
+end;
+
 function TJSParser.ParseExpressionStatement : TJSElement;
 
 Var
@@ -2034,21 +2044,27 @@ Var
   E : TJSElement;
   Done : Boolean;
   VS : TJSElementNodes;
+  aSync : Boolean;
 begin
   {$ifdef debugparser} Writeln('>>> Entering source elements');{$endif}
   Result:=TJSSourceElements(CreateElement(TJSSourceElements));
   try
     Done:=False;
+    aSync:=False;
     VS:=FCurrentVars;
     Try
       FCurrentVars:=Result.Vars;
       Repeat
         {$ifdef debugparser} Writeln('Sourceelements start:',GetEnumName(TypeInfo(TJSToken),Ord(CurrentToken)), ' As string: ',CurrentTokenString);{$endif debugparser}
+        aSync:= (ECMAVersion>=MinAsyncVersion) and (CurrentToken=tjsIdentifier) and (CurrentTokenString='async');
+        if aSync then
+          GetNextToken;
         If (CurrentToken=jstoken.tjsFunction) then
           begin
           If (PeekNextToken<>tjsBraceOpen) then
             begin
             F:=Self.ParseFunctionDeclaration;
+            F.AFunction.IsAsync:=aSync;
             Result.Functions.AddNode.Node:=F;
             end
           else

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

@@ -113,8 +113,8 @@ Type
     procedure Error(const Msg: string);overload;
     procedure Error(const Msg: string; Args: array of Const);overload;
   public
-    constructor Create(ALineReader: TLineReader; ECMAVersion : TECMAVersion = ecma5);
-    constructor Create(AStream : TStream; ECMAVersion : TECMAVersion = ecma5);
+    constructor Create(ALineReader: TLineReader; aECMAVersion : TECMAVersion = ecma5);
+    constructor Create(AStream : TStream; aECMAVersion : TECMAVersion = ecma5);
     destructor Destroy; override;
     procedure OpenFile(const AFilename: string);
     Function FetchRegexprToken: TJSToken;
@@ -162,18 +162,18 @@ begin
   ReadLn(FTextFile, Result);
 end;
 
-constructor TJSScanner.Create(ALineReader: TLineReader; ECMAVersion: TECMAVersion);
+constructor TJSScanner.Create(ALineReader: TLineReader; aECMAVersion: TECMAVersion);
 begin
   inherited Create;
   FSourceFile := ALineReader;
-  FNonKeyWords:=NonJSKeywords[ECMAVersion];
+  ECMAVersion:=aECMAVersion;
 end;
 
-constructor TJSScanner.Create(AStream: TStream; ECMAVersion: TECMAVersion);
+constructor TJSScanner.Create(AStream: TStream; aECMAVersion: TECMAVersion);
 begin
   FSourceStream:=ASTream;
   FOwnSourceFile:=True;
-  Create(TStreamLineReader.Create(AStream));
+  Create(TStreamLineReader.Create(AStream),aECMAVersion);
 end;
 
 destructor TJSScanner.Destroy;

+ 33 - 4
packages/fcl-js/tests/tcparser.pp

@@ -5,7 +5,7 @@ unit tcparser;
 interface
 
 uses
-  Classes, SysUtils, fpcunit, testregistry, jsParser, jstree, jsbase;
+  Classes, SysUtils, fpcunit, testregistry,  jsParser, jstree, jsbase;
 
 type
 
@@ -20,7 +20,7 @@ type
   protected
     procedure SetUp; override; 
     procedure TearDown; override;
-    Procedure CreateParser(Const ASource : string);
+    Procedure CreateParser(Const ASource : string; aVersion : TECMAVersion = TECMAVersion.ecma5);
     Procedure CheckClass(E : TJSElement; C : TJSElementClass);
     Procedure AssertEquals(Const AMessage : String; Expected, Actual : TJSType); overload;
     Procedure AssertIdentifier(Msg : String; El : TJSElement; Const AName : TJSString);
@@ -106,6 +106,7 @@ type
     procedure TestBlockEmptyStatement;
     procedure TestBlockSimpleStatement;
     procedure TestFunctionDeclarationEmpty;
+    procedure TestFunctionDeclarationAsync;
     procedure TestFunctionDeclarationWithArgs;
     procedure TestFunctionDeclarationWithBody;
     procedure TestIfSimple;
@@ -1691,6 +1692,7 @@ begin
   CheckClass(N,TJSFunctionDeclarationStatement);
   FD:=TJSFunctionDeclarationStatement(N);
   AssertNotNull('Function definition assigned',FD.AFunction);
+  AssertFalse('Async function ',FD.AFunction.IsAsync);
   AssertEquals('Function name OK','a',FD.AFunction.Name);
   AssertNotNull('Function body assigned', FD.AFunction.Body);
   AssertEquals('No parameters',0,FD.AFunction.Params.Count);
@@ -1703,6 +1705,33 @@ begin
 //  TJSEmptyBlockStatement
 end;
 
+procedure TTestJSParser.TestFunctionDeclarationAsync;
+Var
+  E : TJSSourceElements;
+  N : TJSElement;
+  FD : TJSFunctionDeclarationStatement;
+
+begin
+  CreateParser('async function a () {}',MinAsyncVersion);
+  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);
+  AssertTrue('Async function ',FD.AFunction.IsAsync);
+  AssertEquals('Function name OK','a',FD.AFunction.Name);
+  AssertNotNull('Function body assigned', FD.AFunction.Body);
+  AssertEquals('No parameters',0,FD.AFunction.Params.Count);
+  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);
+end;
+
 procedure TTestJSParser.TestFunctionDeclarationWithArgs;
 
 Var
@@ -2521,10 +2550,10 @@ begin
   FReeAndNil(FSource);
 end;
 
-Procedure TTestJSParser.CreateParser(Const ASource: string);
+Procedure TTestJSParser.CreateParser(Const ASource: string; aVersion : TECMAVersion = TECMAVersion.ecma5);
 begin
   FSource:=TStringStream.Create(ASource);
-  FParser:=TJSParser.Create(FSource);
+  FParser:=TJSParser.Create(FSource,aVersion);
 end;
 
 Procedure TTestJSParser.CheckClass(E: TJSElement; C: TJSElementClass);