Skip to content

Commit 3a8c8b6

Browse files
committed
Add support for async function specifier and await keyword
Closes: erikd/hjsmin#35
1 parent 359e753 commit 3a8c8b6

File tree

8 files changed

+59
-16
lines changed

8 files changed

+59
-16
lines changed

src/Language/JavaScript/Parser/AST.hs

+4
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ data JSStatement
146146
| JSForConstOf !JSAnnot !JSAnnot !JSAnnot !JSExpression !JSBinOp !JSExpression !JSAnnot !JSStatement -- ^for,lb,var,vardecl,in,expr,rb,stmt
147147
| JSForOf !JSAnnot !JSAnnot !JSExpression !JSBinOp !JSExpression !JSAnnot !JSStatement -- ^for,lb,expr,in,expr,rb,stmt
148148
| JSForVarOf !JSAnnot !JSAnnot !JSAnnot !JSExpression !JSBinOp !JSExpression !JSAnnot !JSStatement -- ^for,lb,var,vardecl,in,expr,rb,stmt
149+
| JSAsyncFunction !JSAnnot !JSAnnot !JSIdent !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSBlock !JSSemi -- ^fn,name, lb,parameter list,rb,block,autosemi
149150
| JSFunction !JSAnnot !JSIdent !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSBlock !JSSemi -- ^fn,name, lb,parameter list,rb,block,autosemi
150151
| JSGenerator !JSAnnot !JSAnnot !JSIdent !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSBlock !JSSemi -- ^fn,*,name, lb,parameter list,rb,block,autosemi
151152
| JSIf !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSStatement -- ^if,(,expr,),stmt
@@ -177,6 +178,7 @@ data JSExpression
177178
-- | Non Terminals
178179
| JSArrayLiteral !JSAnnot ![JSArrayElement] !JSAnnot -- ^lb, contents, rb
179180
| JSAssignExpression !JSExpression !JSAssignOp !JSExpression -- ^lhs, assignop, rhs
181+
| JSAwaitExpression !JSAnnot !JSExpression -- ^await, expr
180182
| JSCallExpression !JSExpression !JSAnnot !(JSCommaList JSExpression) !JSAnnot -- ^expr, bl, args, rb
181183
| JSCallExpressionDot !JSExpression !JSAnnot !JSExpression -- ^expr, dot, expr
182184
| JSCallExpressionSquare !JSExpression !JSAnnot !JSExpression !JSAnnot -- ^expr, [, expr, ]
@@ -391,6 +393,7 @@ instance ShowStripped JSStatement where
391393
ss (JSForOf _ _lb x1s _i x2 _rb x3) = "JSForOf " ++ ss x1s ++ " (" ++ ss x2 ++ ") (" ++ ss x3 ++ ")"
392394
ss (JSForVarOf _ _lb _v x1 _i x2 _rb x3) = "JSForVarOf (" ++ ss x1 ++ ") (" ++ ss x2 ++ ") (" ++ ss x3 ++ ")"
393395
ss (JSFunction _ n _lb pl _rb x3 _) = "JSFunction " ++ ssid n ++ " " ++ ss pl ++ " (" ++ ss x3 ++ ")"
396+
ss (JSAsyncFunction _ _ n _lb pl _rb x3 _) = "JSAsyncFunction " ++ ssid n ++ " " ++ ss pl ++ " (" ++ ss x3 ++ ")"
394397
ss (JSGenerator _ _ n _lb pl _rb x3 _) = "JSGenerator " ++ ssid n ++ " " ++ ss pl ++ " (" ++ ss x3 ++ ")"
395398
ss (JSIf _ _lb x1 _rb x2) = "JSIf (" ++ ss x1 ++ ") (" ++ ss x2 ++ ")"
396399
ss (JSIfElse _ _lb x1 _rb x2 _e x3) = "JSIfElse (" ++ ss x1 ++ ") (" ++ ss x2 ++ ") (" ++ ss x3 ++ ")"
@@ -412,6 +415,7 @@ instance ShowStripped JSStatement where
412415
instance ShowStripped JSExpression where
413416
ss (JSArrayLiteral _lb xs _rb) = "JSArrayLiteral " ++ ss xs
414417
ss (JSAssignExpression lhs op rhs) = "JSOpAssign (" ++ ss op ++ "," ++ ss lhs ++ "," ++ ss rhs ++ ")"
418+
ss (JSAwaitExpression _ e) = "JSAwaitExpresson " ++ ss e
415419
ss (JSCallExpression ex _ xs _) = "JSCallExpression ("++ ss ex ++ ",JSArguments " ++ ss xs ++ ")"
416420
ss (JSCallExpressionDot ex _os xs) = "JSCallExpressionDot (" ++ ss ex ++ "," ++ ss xs ++ ")"
417421
ss (JSCallExpressionSquare ex _os xs _cs) = "JSCallExpressionSquare (" ++ ss ex ++ "," ++ ss xs ++ ")"

src/Language/JavaScript/Parser/Grammar7.y

+39-15
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ import qualified Language.JavaScript.Parser.AST as AST
8686

8787
'as' { AsToken {} }
8888
'autosemi' { AutoSemiToken {} }
89+
'async' { AsyncToken {} }
90+
'await' { AwaitToken {} }
8991
'break' { BreakToken {} }
9092
'case' { CaseToken {} }
9193
'catch' { CatchToken {} }
@@ -331,6 +333,8 @@ OpAssign : '*=' { AST.JSTimesAssign (mkJSAnnot $1) }
331333
-- TODO: make this include any reserved word too, including future ones
332334
IdentifierName :: { AST.JSExpression }
333335
IdentifierName : Identifier {$1}
336+
| 'async' { AST.JSIdentifier (mkJSAnnot $1) "async" }
337+
| 'await' { AST.JSIdentifier (mkJSAnnot $1) "await" }
334338
| 'break' { AST.JSIdentifier (mkJSAnnot $1) "break" }
335339
| 'case' { AST.JSIdentifier (mkJSAnnot $1) "case" }
336340
| 'catch' { AST.JSIdentifier (mkJSAnnot $1) "catch" }
@@ -407,6 +411,12 @@ For : 'for' { mkJSAnnot $1 }
407411
Continue :: { AST.JSAnnot }
408412
Continue : 'continue' { mkJSAnnot $1 }
409413

414+
Async :: { AST.JSAnnot }
415+
Async : 'async' { mkJSAnnot $1 }
416+
417+
Await :: { AST.JSAnnot }
418+
Await : 'await' { mkJSAnnot $1 }
419+
410420
Break :: { AST.JSAnnot }
411421
Break : 'break' { mkJSAnnot $1 }
412422

@@ -657,6 +667,10 @@ NewExpression :: { AST.JSExpression }
657667
NewExpression : MemberExpression { $1 {- 'NewExpression1' -} }
658668
| New NewExpression { AST.JSNewExpression $1 $2 {- 'NewExpression2' -} }
659669

670+
AwaitExpression :: { AST.JSExpression }
671+
AwaitExpression
672+
: Await Expression { AST.JSAwaitExpression $1 $2 }
673+
660674
-- CallExpression : See 11.2
661675
-- MemberExpression Arguments
662676
-- CallExpression Arguments
@@ -696,6 +710,7 @@ ArgumentList : AssignmentExpression { AST.JSLOne $1
696710
LeftHandSideExpression :: { AST.JSExpression }
697711
LeftHandSideExpression : NewExpression { $1 {- 'LeftHandSideExpression1' -} }
698712
| CallExpression { $1 {- 'LeftHandSideExpression12' -} }
713+
| AwaitExpression { $1 {- 'LeftHandSideExpression13' -} }
699714

700715
-- PostfixExpression : See 11.3
701716
-- LeftHandSideExpression
@@ -976,20 +991,23 @@ Statement : StatementNoEmpty { $1 {- 'Statement1' -} }
976991
| EmptyStatement { $1 {- 'Statement2' -} }
977992

978993
StatementNoEmpty :: { AST.JSStatement }
979-
StatementNoEmpty : StatementBlock { $1 {- 'StatementNoEmpty1' -} }
980-
| VariableStatement { $1 {- 'StatementNoEmpty2' -} }
981-
| ExpressionStatement { $1 {- 'StatementNoEmpty4' -} }
982-
| IfStatement { $1 {- 'StatementNoEmpty5' -} }
983-
| IterationStatement { $1 {- 'StatementNoEmpty6' -} }
984-
| ContinueStatement { $1 {- 'StatementNoEmpty7' -} }
985-
| BreakStatement { $1 {- 'StatementNoEmpty8' -} }
986-
| ReturnStatement { $1 {- 'StatementNoEmpty9' -} }
987-
| WithStatement { $1 {- 'StatementNoEmpty10' -} }
988-
| LabelledStatement { $1 {- 'StatementNoEmpty11' -} }
989-
| SwitchStatement { $1 {- 'StatementNoEmpty12' -} }
990-
| ThrowStatement { $1 {- 'StatementNoEmpty13' -} }
991-
| TryStatement { $1 {- 'StatementNoEmpty14' -} }
992-
| DebuggerStatement { $1 {- 'StatementNoEmpty15' -} }
994+
StatementNoEmpty
995+
: IfStatement { $1 {- 'StatementNoEmpty5' -} }
996+
| ContinueStatement { $1 {- 'StatementNoEmpty7' -} }
997+
| BreakStatement { $1 {- 'StatementNoEmpty8' -} }
998+
| ReturnStatement { $1 {- 'StatementNoEmpty9' -} }
999+
| WithStatement { $1 {- 'StatementNoEmpty10' -} }
1000+
| LabelledStatement { $1 {- 'StatementNoEmpty11' -} }
1001+
| SwitchStatement { $1 {- 'StatementNoEmpty12' -} }
1002+
| ThrowStatement { $1 {- 'StatementNoEmpty13' -} }
1003+
| TryStatement { $1 {- 'StatementNoEmpty14' -} }
1004+
| StatementBlock { $1 {- 'StatementNoEmpty1' -} }
1005+
| VariableStatement { $1 {- 'StatementNoEmpty2' -} }
1006+
| IterationStatement { $1 {- 'StatementNoEmpty6' -} }
1007+
| ExpressionStatement { $1 {- 'StatementNoEmpty4' -} }
1008+
| AsyncFunctionStatement { $1 {- 'StatementNoEmpty15' -} }
1009+
| DebuggerStatement { $1 {- 'StatementNoEmpty15' -} }
1010+
9931011

9941012

9951013
StatementBlock :: { AST.JSStatement }
@@ -1208,7 +1226,10 @@ DebuggerStatement : 'debugger' MaybeSemi { AST.JSExpressionStatement (AST.JSLite
12081226
-- FunctionDeclaration : See clause 13
12091227
-- function Identifier ( FormalParameterListopt ) { FunctionBody }
12101228
FunctionDeclaration :: { AST.JSStatement }
1211-
FunctionDeclaration : NamedFunctionExpression MaybeSemi { expressionToStatement $1 $2 {- 'FunctionDeclaration1' -} }
1229+
FunctionDeclaration : NamedFunctionExpression MaybeSemi { expressionToStatement $1 $2 {- 'FunctionDeclaration1' -} }
1230+
1231+
AsyncFunctionStatement :: { AST.JSStatement }
1232+
AsyncFunctionStatement : Async NamedFunctionExpression MaybeSemi { expressionToAsyncFunction $1 $2 $3 {- 'AsyncFunctionStatement1' -} }
12121233

12131234
-- FunctionExpression : See clause 13
12141235
-- function Identifieropt ( FormalParameterListopt ) { FunctionBody }
@@ -1499,6 +1520,9 @@ expressionToStatement (AST.JSMemberExpression e l a r) s = AST.JSMethodCall e l
14991520
expressionToStatement (AST.JSClassExpression a b@(AST.JSIdentName{}) c d e f) s = AST.JSClass a b c d e f s
15001521
expressionToStatement exp s = AST.JSExpressionStatement exp s
15011522

1523+
expressionToAsyncFunction :: AST.JSAnnot -> AST.JSExpression -> AST.JSSemi -> AST.JSStatement
1524+
expressionToAsyncFunction aa (AST.JSFunctionExpression a b@(AST.JSIdentName{}) c d e f) s = AST.JSAsyncFunction aa a b c d e f s
1525+
expressionToAsyncFunction _aa _exp _s = error "Bad async function."
15021526

15031527
mkJSCallExpression :: AST.JSExpression -> JSArguments -> AST.JSExpression
15041528
mkJSCallExpression e (JSArguments l arglist r) = AST.JSCallExpression e l arglist r

src/Language/JavaScript/Parser/Lexer.x

+3-1
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,9 @@ keywords = Map.fromList keywordNames
529529
530530
keywordNames :: [(String, TokenPosn -> String -> [CommentAnnotation] -> Token)]
531531
keywordNames =
532-
[ ( "break", BreakToken )
532+
[ ( "async", AsyncToken )
533+
, ( "await", AwaitToken )
534+
, ( "break", BreakToken )
533535
, ( "case", CaseToken )
534536
, ( "catch", CatchToken )
535537

src/Language/JavaScript/Parser/Token.hs

+2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ data Token
5656
-- ^ Literal: Regular Expression
5757

5858
-- Keywords
59+
| AsyncToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
60+
| AwaitToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
5961
| BreakToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
6062
| CaseToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
6163
| CatchToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }

src/Language/JavaScript/Pretty/Printer.hs

+2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ instance RenderJS JSExpression where
7676
(|>) pacc (JSArrayLiteral als xs ars) = pacc |> als |> "[" |> xs |> ars |> "]"
7777
(|>) pacc (JSArrowExpression xs a x) = pacc |> xs |> a |> "=>" |> x
7878
(|>) pacc (JSAssignExpression lhs op rhs) = pacc |> lhs |> op |> rhs
79+
(|>) pacc (JSAwaitExpression a e) = pacc |> a |> "await" |> e
7980
(|>) pacc (JSCallExpression ex lb xs rb) = pacc |> ex |> lb |> "(" |> xs |> rb |> ")"
8081
(|>) pacc (JSCallExpressionDot ex os xs) = pacc |> ex |> os |> "." |> xs
8182
(|>) pacc (JSCallExpressionSquare ex als xs ars) = pacc |> ex |> als |> "[" |> xs |> ars |> "]"
@@ -244,6 +245,7 @@ instance RenderJS JSStatement where
244245
(|>) pacc (JSForConstOf af alb v x1 i x2 arb x3) = pacc |> af |> "for" |> alb |> "(" |> "const" |> v |> x1 |> i |> x2 |> arb |> ")" |> x3
245246
(|>) pacc (JSForOf af alb x1s i x2 arb x3) = pacc |> af |> "for" |> alb |> "(" |> x1s |> i |> x2 |> arb |> ")" |> x3
246247
(|>) pacc (JSForVarOf af alb v x1 i x2 arb x3) = pacc |> af |> "for" |> alb |> "(" |> "var" |> v |> x1 |> i |> x2 |> arb |> ")" |> x3
248+
(|>) pacc (JSAsyncFunction aa af n alb x2s arb x3 s) = pacc |> aa |> "async" |> af |> "function" |> n |> alb |> "(" |> x2s |> arb |> ")" |> x3 |> s
247249
(|>) pacc (JSFunction af n alb x2s arb x3 s) = pacc |> af |> "function" |> n |> alb |> "(" |> x2s |> arb |> ")" |> x3 |> s
248250
(|>) pacc (JSGenerator af as n alb x2s arb x3 s) = pacc |> af |> "function" |> as |> "*" |> n |> alb |> "(" |> x2s |> arb |> ")" |> x3 |> s
249251
(|>) pacc (JSIf annot alb x1 arb x2s) = pacc |> annot |> "if" |> alb |> "(" |> x1 |> arb |> ")" |> x2s

src/Language/JavaScript/Process/Minify.hs

+2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ fixStmt a s (JSForConstIn _ _ _ e1 op e2 _ st) = JSForConstIn a emptyAnnot space
5959
fixStmt a s (JSForConstOf _ _ _ e1 op e2 _ st) = JSForConstOf a emptyAnnot spaceAnnot (fixEmpty e1) (fixSpace op) (fixSpace e2) emptyAnnot (fixStmtE s st)
6060
fixStmt a s (JSForOf _ _ e1 op e2 _ st) = JSForOf a emptyAnnot (fixEmpty e1) (fixSpace op) (fixSpace e2) emptyAnnot (fixStmtE s st)
6161
fixStmt a s (JSForVarOf _ _ _ e1 op e2 _ st) = JSForVarOf a emptyAnnot spaceAnnot (fixEmpty e1) (fixSpace op) (fixSpace e2) emptyAnnot (fixStmtE s st)
62+
fixStmt a s (JSAsyncFunction _ _ n _ ps _ blk _) = JSAsyncFunction a spaceAnnot (fixSpace n) emptyAnnot (fixEmpty ps) emptyAnnot (fixEmpty blk) s
6263
fixStmt a s (JSFunction _ n _ ps _ blk _) = JSFunction a (fixSpace n) emptyAnnot (fixEmpty ps) emptyAnnot (fixEmpty blk) s
6364
fixStmt a s (JSGenerator _ _ n _ ps _ blk _) = JSGenerator a emptyAnnot (fixEmpty n) emptyAnnot (fixEmpty ps) emptyAnnot (fixEmpty blk) s
6465
fixStmt a s (JSIf _ _ e _ st) = JSIf a emptyAnnot (fixEmpty e) emptyAnnot (fixIfElseBlock emptyAnnot s st)
@@ -157,6 +158,7 @@ instance MinifyJS JSExpression where
157158
fix _ (JSArrayLiteral _ xs _) = JSArrayLiteral emptyAnnot (map fixEmpty xs) emptyAnnot
158159
fix a (JSArrowExpression ps _ ss) = JSArrowExpression (fix a ps) emptyAnnot (fixStmt emptyAnnot noSemi ss)
159160
fix a (JSAssignExpression lhs op rhs) = JSAssignExpression (fix a lhs) (fixEmpty op) (fixEmpty rhs)
161+
fix a (JSAwaitExpression _ ex) = JSAwaitExpression a (fixSpace ex)
160162
fix a (JSCallExpression ex _ xs _) = JSCallExpression (fix a ex) emptyAnnot (fixEmpty xs) emptyAnnot
161163
fix a (JSCallExpressionDot ex _ xs) = JSCallExpressionDot (fix a ex) emptyAnnot (fixEmpty xs)
162164
fix a (JSCallExpressionSquare ex _ xs _) = JSCallExpressionSquare (fix a ex) emptyAnnot (fixEmpty xs) emptyAnnot

test/Test/Language/Javascript/Lexer.hs

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ testLexer = describe "Lexer:" $ do
6969
testLex "in\n" `shouldBe` "[InToken,WsToken]"
7070
testLex "of\n" `shouldBe` "[OfToken,WsToken]"
7171

72+
it "function" $ do
73+
testLex "async function\n" `shouldBe` "[AsyncToken,WsToken,FunctionToken,WsToken]"
74+
7275

7376
testLex :: String -> String
7477
testLex str =

test/Test/Language/Javascript/Minify.hs

+4
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ testMinifyStmt = describe "Minify statements:" $ do
232232
minifyStmt " function f ( a = 1 , b = 2 ) { return a + b ; } ; " `shouldBe` "function f(a=1,b=2){return a+b}"
233233
minifyStmt " function f ( [ a , b ] ) { return a + b ; } ; " `shouldBe` "function f([a,b]){return a+b}"
234234
minifyStmt " function f ( { a , b , } ) { return a + b ; } ; " `shouldBe` "function f({a,b}){return a+b}"
235+
minifyStmt " async function f ( ) { } " `shouldBe` "async function f(){}"
235236

236237
it "generator" $ do
237238
minifyStmt " function * f ( ) { } ; " `shouldBe` "function*f(){}"
@@ -279,6 +280,9 @@ testMinifyStmt = describe "Minify statements:" $ do
279280
minifyStmt " class Foo extends (getBase()) {} " `shouldBe` "class Foo extends(getBase()){}"
280281
minifyStmt " class Foo extends [ Bar1, Bar2 ][getBaseIndex()] {} " `shouldBe` "class Foo extends[Bar1,Bar2][getBaseIndex()]{}"
281282

283+
it "miscellaneous" $
284+
minifyStmt " let r = await p ; " `shouldBe` "let r=await p"
285+
282286
testMinifyProg :: Spec
283287
testMinifyProg = describe "Minify programs:" $ do
284288
it "simple" $ do

0 commit comments

Comments
 (0)