Skip to content

Commit eb2b798

Browse files
committed
Parse export lists and variable statements
1 parent f082ec6 commit eb2b798

File tree

9 files changed

+89
-19
lines changed

9 files changed

+89
-19
lines changed

src/Language/JavaScript/Parser/AST.hs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ module Language.JavaScript.Parser.AST
1010
, JSTryCatch (..)
1111
, JSTryFinally (..)
1212
, JSStatement (..)
13+
, JSExportBody (..)
14+
, JSExportSpecifier (..)
1315
, JSBlock (..)
1416
, JSSwitchParts (..)
1517
, JSAST (..)
@@ -74,7 +76,7 @@ data JSStatement
7476
| JSVariable !JSAnnot !(JSCommaList JSExpression) !JSSemi -- ^var, decl, autosemi
7577
| JSWhile !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSStatement -- ^while,lb,expr,rb,stmt
7678
| JSWith !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSStatement !JSSemi -- ^with,lb,expr,rb,stmt list
77-
| JSExport !JSAnnot !(Maybe JSExpression) !JSSemi -- ^export,expr
79+
| JSExport !JSAnnot !JSExportBody !JSSemi -- ^export, body, autosemi
7880
deriving (Data, Eq, Show, Typeable)
7981

8082
data JSExpression
@@ -113,6 +115,7 @@ data JSExpression
113115

114116
data JSBinOp
115117
= JSBinOpAnd !JSAnnot
118+
| JSBinOpAs !JSAnnot
116119
| JSBinOpBitAnd !JSAnnot
117120
| JSBinOpBitOr !JSAnnot
118121
| JSBinOpBitXor !JSAnnot
@@ -169,6 +172,16 @@ data JSAssignOp
169172
| JSBwOrAssign !JSAnnot
170173
deriving (Data, Eq, Show, Typeable)
171174

175+
data JSExportBody
176+
= JSExportStatement !JSStatement
177+
| JSExportClause !JSAnnot !(Maybe (JSCommaList JSExportSpecifier)) !JSAnnot -- ^lb,body,rb
178+
deriving (Data, Eq, Show, Typeable)
179+
180+
data JSExportSpecifier
181+
= JSExportSpecifier !JSIdent
182+
| JSExportSpecifierAs !JSIdent !JSBinOp !JSIdent
183+
deriving (Data, Eq, Show, Typeable)
184+
172185
data JSTryCatch
173186
= JSCatch !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSBlock -- ^catch,lb,ident,rb,block
174187
| JSCatchIf !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSExpression !JSAnnot !JSBlock -- ^catch,lb,ident,if,expr,rb,block
@@ -247,7 +260,6 @@ showStripped (JSAstLiteral s _) = "JSAstLiteral (" ++ ss s ++ ")"
247260
class ShowStripped a where
248261
ss :: a -> String
249262

250-
251263
instance ShowStripped JSStatement where
252264
ss (JSStatementBlock _ xs _ _) = "JSStatementBlock " ++ ss xs
253265
ss (JSBreak _ JSIdentNone s) = "JSBreak" ++ commaIf (ss s)
@@ -277,8 +289,7 @@ instance ShowStripped JSStatement where
277289
ss (JSVariable _ xs _as) = "JSVariable " ++ ss xs
278290
ss (JSWhile _ _lb x1 _rb x2) = "JSWhile (" ++ ss x1 ++ ") (" ++ ss x2 ++ ")"
279291
ss (JSWith _ _lb x1 _rb x _) = "JSWith (" ++ ss x1 ++ ") (" ++ ss x ++ ")"
280-
ss (JSExport _ Nothing _) = "JSExport"
281-
ss (JSExport _ (Just x1) _) = "JSExport (" ++ (ss x1) ++ ")"
292+
ss (JSExport _ b _) = "JSExport (" ++ ss b ++ ")"
282293

283294
instance ShowStripped JSExpression where
284295
ss (JSArrayLiteral _lb xs _rb) = "JSArrayLiteral " ++ ss xs
@@ -311,6 +322,15 @@ instance ShowStripped JSExpression where
311322
ss (JSVarInitExpression x1 x2) = "JSVarInitExpression (" ++ ss x1 ++ ") " ++ ss x2
312323
ss (JSSpreadExpression _ x1) = "JSSpreadExpression (" ++ ss x1 ++ ")"
313324

325+
instance ShowStripped JSExportBody where
326+
ss (JSExportStatement x1) = "JSExportStatement (" ++ ss x1 ++ ")"
327+
ss (JSExportClause _ Nothing _) = "JSExportClause ()"
328+
ss (JSExportClause _ (Just x1) _) = "JSExportClause (" ++ ss x1 ++ ")"
329+
330+
instance ShowStripped JSExportSpecifier where
331+
ss (JSExportSpecifier x1) = "JSExportSpecifier (" ++ ss x1 ++ ")"
332+
ss (JSExportSpecifierAs x1 _ x2) = "JSExportSpecifierAs (" ++ ss x1 ++ "," ++ ss x2 ++ ")"
333+
314334
instance ShowStripped JSTryCatch where
315335
ss (JSCatch _ _lb x1 _rb x3) = "JSCatch (" ++ ss x1 ++ "," ++ ss x3 ++ ")"
316336
ss (JSCatchIf _ _lb x1 _ ex _rb x3) = "JSCatch (" ++ ss x1 ++ ") if " ++ ss ex ++ " (" ++ ss x3 ++ ")"
@@ -345,6 +365,7 @@ instance ShowStripped JSSwitchParts where
345365

346366
instance ShowStripped JSBinOp where
347367
ss (JSBinOpAnd _) = "'&&'"
368+
ss (JSBinOpAs _) = "'as'"
348369
ss (JSBinOpBitAnd _) = "'&'"
349370
ss (JSBinOpBitOr _) = "'|'"
350371
ss (JSBinOpBitXor _) = "'^'"
@@ -440,6 +461,7 @@ commaIf xs = ',' : xs
440461

441462
deAnnot :: JSBinOp -> JSBinOp
442463
deAnnot (JSBinOpAnd _) = JSBinOpAnd JSNoAnnot
464+
deAnnot (JSBinOpAs _) = JSBinOpAs JSNoAnnot
443465
deAnnot (JSBinOpBitAnd _) = JSBinOpBitAnd JSNoAnnot
444466
deAnnot (JSBinOpBitOr _) = JSBinOpBitOr JSNoAnnot
445467
deAnnot (JSBinOpBitXor _) = JSBinOpBitXor JSNoAnnot

src/Language/JavaScript/Parser/Grammar7.y

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ import qualified Language.JavaScript.Parser.AST as AST
8181
'(' { LeftParenToken {} }
8282
')' { RightParenToken {} }
8383

84+
'as' { AsToken {} }
8485
'autosemi' { AutoSemiToken {} }
8586
'break' { BreakToken {} }
8687
'case' { CaseToken {} }
@@ -245,6 +246,9 @@ Ge : '>=' { AST.JSBinOpGe (mkJSAnnot $1) }
245246
Gt :: { AST.JSBinOp }
246247
Gt : '>' { AST.JSBinOpGt (mkJSAnnot $1) }
247248

249+
As :: { AST.JSBinOp }
250+
As : 'as' { AST.JSBinOpAs (mkJSAnnot $1) }
251+
248252
In :: { AST.JSBinOp }
249253
In : 'in' { AST.JSBinOpIn (mkJSAnnot $1) }
250254

@@ -430,6 +434,7 @@ Identifier : 'ident' { AST.JSIdentifier (mkJSAnnot $1) (tokenLiteral $1) }
430434
-- TODO: make this include any reserved word too, including future ones
431435
IdentifierName :: { AST.JSExpression }
432436
IdentifierName : Identifier {$1}
437+
| 'as' { AST.JSIdentifier (mkJSAnnot $1) "as" }
433438
| 'break' { AST.JSIdentifier (mkJSAnnot $1) "break" }
434439
| 'case' { AST.JSIdentifier (mkJSAnnot $1) "case" }
435440
| 'catch' { AST.JSIdentifier (mkJSAnnot $1) "catch" }
@@ -1152,30 +1157,39 @@ Program : StatementList Eof { AST.JSAstProgram $1 $2 {- 'Program1' -} }
11521157
| Eof { AST.JSAstProgram [] $1 {- 'Program2' -} }
11531158
11541159
-- ExportDeclaration : See 15.2.3
1155-
-- export * FromClause ;
1156-
-- export ExportClause FromClause ;
1157-
-- export ExportClause ;
1158-
-- export VariableStatement
1159-
-- export Declaration
1160-
-- export default HoistableDeclaration[Default]
1161-
-- export default ClassDeclaration[Default]
1162-
-- export default [lookahead ∉ { function, class }] AssignmentExpression[In] ;
1160+
-- [ ] export * FromClause ;
1161+
-- [ ] export ExportClause FromClause ;
1162+
-- [x] export ExportClause ;
1163+
-- [x] export VariableStatement
1164+
-- [ ] export Declaration
1165+
-- [ ] export default HoistableDeclaration[Default]
1166+
-- [ ] export default ClassDeclaration[Default]
1167+
-- [ ] export default [lookahead ∉ { function, class }] AssignmentExpression[In] ;
11631168
ExportDeclaration :: { AST.JSStatement }
1164-
ExportDeclaration : Export ExportClause AutoSemi { AST.JSExport $1 $2 $3 {- 'ExportDeclaration' -} }
1169+
ExportDeclaration : Export ExportClause AutoSemi { AST.JSExport $1 $2 $3 {- 'ExportDeclaration1' -} }
1170+
| Export VariableStatement AutoSemi { AST.JSExport $1 (AST.JSExportStatement $2) $3 {- 'ExportDeclaration2' -} }
11651171
11661172
-- ExportClause :
11671173
-- { }
11681174
-- { ExportsList }
11691175
-- { ExportsList , }
1170-
ExportClause :: { Maybe AST.JSExpression }
1171-
ExportClause : LBrace RBrace { Nothing {- 'ExportClause1' -} }
1176+
ExportClause :: { AST.JSExportBody }
1177+
ExportClause : LBrace RBrace { AST.JSExportClause $1 Nothing $2 {- 'ExportClause1' -} }
1178+
| LBrace ExportsList RBrace { AST.JSExportClause $1 (Just $2) $3 {- 'ExportClause2' -} }
11721179
11731180
-- ExportsList :
11741181
-- ExportSpecifier
11751182
-- ExportsList , ExportSpecifier
1183+
ExportsList :: { AST.JSCommaList AST.JSExportSpecifier }
1184+
ExportsList : ExportSpecifier { AST.JSLOne $1 {- 'ExportsList1' -} }
1185+
| ExportsList Comma ExportSpecifier { AST.JSLCons $1 $2 $3 {- 'ExportsList2' -} }
1186+
11761187
-- ExportSpecifier :
11771188
-- IdentifierName
11781189
-- IdentifierName as IdentifierName
1190+
ExportSpecifier :: { AST.JSExportSpecifier }
1191+
ExportSpecifier : IdentifierName { AST.JSExportSpecifier (identName $1) {- 'ExportSpecifier1' -} }
1192+
| IdentifierName As IdentifierName { AST.JSExportSpecifierAs (identName $1) $2 (identName $3) {- 'ExportSpecifier2' -} }
11791193
11801194
-- For debugging/other entry points
11811195
LiteralMain :: { AST.JSAST }

src/Language/JavaScript/Parser/Lexer.x

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,8 @@ keywords = Map.fromList keywordNames
503503
504504
keywordNames :: [(String, TokenPosn -> String -> [CommentAnnotation] -> Token)]
505505
keywordNames =
506-
[ ( "break", BreakToken )
506+
[ ( "as", AsToken )
507+
, ( "break", BreakToken )
507508
, ( "case", CaseToken )
508509
, ( "catch", CatchToken )
509510

src/Language/JavaScript/Parser/Token.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ data Token
5656
-- ^ Literal: Regular Expression
5757

5858
-- Keywords
59+
| AsToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
5960
| BreakToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
6061
| CaseToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
6162
| CatchToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }

src/Language/JavaScript/Pretty/Printer.hs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ instance RenderJS [JSExpression] where
138138

139139
instance RenderJS JSBinOp where
140140
(|>) pacc (JSBinOpAnd annot) = pacc |> annot |> "&&"
141+
(|>) pacc (JSBinOpAs annot) = pacc |> annot |> "as"
141142
(|>) pacc (JSBinOpBitAnd annot) = pacc |> annot |> "&"
142143
(|>) pacc (JSBinOpBitOr annot) = pacc |> annot |> "|"
143144
(|>) pacc (JSBinOpBitXor annot) = pacc |> annot |> "^"
@@ -238,6 +239,7 @@ instance RenderJS JSStatement where
238239
(|>) pacc (JSVariable annot xs s) = pacc |> annot |> "var" |> xs |> s
239240
(|>) pacc (JSWhile annot alp x1 arp x2) = pacc |> annot |> "while" |> alp |> "(" |> x1 |> arp |> ")" |> x2
240241
(|>) pacc (JSWith annot alp x1 arp x s) = pacc |> annot |> "with" |> alp |> "(" |> x1 |> arp |> ")" |> x |> s
242+
(|>) pacc (JSExport annot b s) = pacc |> annot |> "export" |> b |> s
241243

242244
instance RenderJS [JSStatement] where
243245
(|>) = foldl' (|>)
@@ -265,6 +267,15 @@ instance RenderJS JSArrayElement where
265267
instance RenderJS [JSArrayElement] where
266268
(|>) = foldl' (|>)
267269

270+
instance RenderJS JSExportBody where
271+
(|>) pacc (JSExportStatement s) = pacc |> " " |> s
272+
(|>) pacc (JSExportClause alb Nothing arb) = pacc |> alb |> "{}" |> arb
273+
(|>) pacc (JSExportClause alb (Just s) arb) = pacc |> alb |> "{" |> s |> "}" |> arb
274+
275+
instance RenderJS JSExportSpecifier where
276+
(|>) pacc (JSExportSpecifier i) = pacc |> i
277+
(|>) pacc (JSExportSpecifierAs x1 as x2) = pacc |> x1 |> as |> x2
278+
268279
instance RenderJS a => RenderJS (JSCommaList a) where
269280
(|>) pacc (JSLCons pl a i) = pacc |> pl |> a |> "," |> i
270281
(|>) pacc (JSLOne i) = pacc |> i
@@ -287,4 +298,3 @@ instance RenderJS JSVarInitializer where
287298
(|>) pacc JSVarInitNone = pacc
288299

289300
-- EOF
290-

src/Language/JavaScript/Process/Minify.hs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ fixStmt a _ (JSTry _ b tc tf) = JSTry a (fixEmpty b) (map fixEmpty tc) (fixEmpty
6464
fixStmt a s (JSVariable _ ss _) = JSVariable a (fixVarList ss) s
6565
fixStmt a s (JSWhile _ _ e _ st) = JSWhile a emptyAnnot (fixEmpty e) emptyAnnot (fixStmt a s st)
6666
fixStmt a s (JSWith _ _ e _ st _) = JSWith a emptyAnnot (fixEmpty e) emptyAnnot (fixStmtE noSemi st) s
67+
fixStmt a s (JSExport _ b _) = JSExport a (fixEmpty b) s
6768

6869

6970
fixIfElseBlock :: JSAnnot -> JSSemi -> JSStatement -> JSStatement
@@ -209,6 +210,7 @@ normalizeToSQ str =
209210

210211
instance MinifyJS JSBinOp where
211212
fix _ (JSBinOpAnd _) = JSBinOpAnd emptyAnnot
213+
fix a (JSBinOpAs _) = JSBinOpAs a
212214
fix _ (JSBinOpBitAnd _) = JSBinOpBitAnd emptyAnnot
213215
fix _ (JSBinOpBitOr _) = JSBinOpBitOr emptyAnnot
214216
fix _ (JSBinOpBitXor _) = JSBinOpBitXor emptyAnnot
@@ -265,6 +267,13 @@ instance MinifyJS JSAssignOp where
265267
fix a (JSBwXorAssign _) = JSBwXorAssign a
266268
fix a (JSBwOrAssign _) = JSBwOrAssign a
267269

270+
instance MinifyJS JSExportBody where
271+
fix a (JSExportStatement s) = JSExportStatement (fixStmt a noSemi s)
272+
fix _ (JSExportClause _ x1 _) = JSExportClause emptyAnnot (fixEmpty <$> x1) emptyAnnot
273+
274+
instance MinifyJS JSExportSpecifier where
275+
fix _ (JSExportSpecifier x1) = JSExportSpecifier (fixEmpty x1)
276+
fix _ (JSExportSpecifierAs x1 as x2) = JSExportSpecifierAs (fixEmpty x1) (fixSpace as) (fixSpace x2)
268277

269278
instance MinifyJS JSTryCatch where
270279
fix a (JSCatch _ _ x1 _ x3) = JSCatch a emptyAnnot (fixEmpty x1) emptyAnnot (fixEmpty x3)

test/Test/Language/Javascript/Minify.hs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,13 @@ testMinifyStmt = describe "Minify statements:" $ do
148148
minifyStmt " { ; e = 1 } " `shouldBe` "e=1"
149149
minifyStmt " { { } ; f = 1 ; { } ; } ; " `shouldBe` "f=1"
150150

151+
it "export" $ do
152+
minifyStmt " export { } ; " `shouldBe` "export{}"
153+
minifyStmt " export { a } ; " `shouldBe` "export{a}"
154+
minifyStmt " export { a, b } ; " `shouldBe` "export{a,b}"
155+
minifyStmt " export { a, b as c , d } ; " `shouldBe` "export{a,b as c,d}"
156+
minifyStmt " export const a = 1 ; " `shouldBe` "export const a=1"
157+
151158
it "if" $ do
152159
minifyStmt " if ( 1 ) return ; " `shouldBe` "if(1)return"
153160
minifyStmt " if ( 1 ) ; " `shouldBe` "if(1);"

test/Test/Language/Javascript/RoundTrip.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ testRoundTrip = describe "Roundtrip:" $ do
9898
testRT "switch (x) {default:break;}"
9999
testRT "switch (x) {default:\ncase 1:break;}"
100100
testRT "var x=1;let y=2;"
101+
-- modules
102+
testRT "export {};"
103+
testRT "export { a, X as B, c}"
101104

102105

103106
testRT :: String -> Expectation

test/Test/Language/Javascript/StatementParser.hs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ testStatementParser = describe "Parse statements:" $ do
1818
testStmt "true?1:2" `shouldBe` "Right (JSAstStatement (JSExpressionTernary (JSLiteral 'true',JSDecimal '1',JSDecimal '2')))"
1919

2020
it "export" $ do
21-
testStmt "export {}" `shouldBe` "Right (JSAstStatement (JSExport))"
22-
testStmt "export {};" `shouldBe` "Right (JSAstStatement (JSExport))"
21+
testStmt "export {}" `shouldBe` "Right (JSAstStatement (JSExport (JSExportClause ())))"
22+
testStmt "export {};" `shouldBe` "Right (JSAstStatement (JSExport (JSExportClause ())))"
23+
testStmt "export const a = 1;" `shouldBe` "Right (JSAstStatement (JSExport (JSExportStatement (JSConstant (JSVarInitExpression (JSIdentifier 'a') [JSDecimal '1'])))))"
24+
testStmt "export { a };" `shouldBe` "Right (JSAstStatement (JSExport (JSExportClause ((JSExportSpecifier (JSIdentifier 'a'))))))"
25+
testStmt "export { X as Y };" `shouldBe` "Right (JSAstStatement (JSExport (JSExportClause ((JSExportSpecifierAs (JSIdentifier 'X',JSIdentifier 'Y'))))))"
2326

2427
it "block" $ do
2528
testStmt "{}" `shouldBe` "Right (JSAstStatement (JSStatementBlock []))"

0 commit comments

Comments
 (0)