diff --git a/packages/fcl-js/src/jsparser.pp b/packages/fcl-js/src/jsparser.pp index dcfb242ceb..d8c0f79ff0 100644 --- a/packages/fcl-js/src/jsparser.pp +++ b/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 diff --git a/packages/fcl-js/src/jsscanner.pp b/packages/fcl-js/src/jsscanner.pp index 00bb98688d..a04646d5e2 100644 --- a/packages/fcl-js/src/jsscanner.pp +++ b/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; diff --git a/packages/fcl-js/tests/tcparser.pp b/packages/fcl-js/tests/tcparser.pp index cd2150de69..3f331f0f2c 100644 --- a/packages/fcl-js/tests/tcparser.pp +++ b/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);