Skip to content

ES2015: Modules: export declarations (partial) #80

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@
/unicode/uc-nl.htm
/unicode/uc-pc.htm
cabal.sandbox.config

# stack
/.stack-work/
5 changes: 4 additions & 1 deletion language-javascript.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ License: BSD3
License-file: LICENSE
Author: Alan Zimmerman
Maintainer: Erik de Castro Lopo <[email protected]>
Copyright: (c) 2010-2015 Alan Zimmerman, 2015 Erik de Castro Lopo
Copyright: (c) 2010-2015 Alan Zimmerman
(c) 2015-2018 Erik de Castro Lopo
(c) 2018 Daniel Gasienica
Category: Language
Build-type: Simple
homepage: https://github.com/erikd/language-javascript
Expand Down Expand Up @@ -81,6 +83,7 @@ Test-Suite testsuite
Test.Language.Javascript.Lexer
Test.Language.Javascript.LiteralParser
Test.Language.Javascript.Minify
Test.Language.Javascript.ModuleParser
Test.Language.Javascript.ProgramParser
Test.Language.Javascript.RoundTrip
Test.Language.Javascript.StatementParser
Expand Down
4 changes: 2 additions & 2 deletions src/Language/JavaScript/Parser.hs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
module Language.JavaScript.Parser
(
PA.parse
, PA.parseModule
, PA.readJs
, PA.readJsModule
, PA.parseFile
, PA.parseFileUtf8
, PA.showStripped
Expand Down Expand Up @@ -40,5 +42,3 @@ import Language.JavaScript.Parser.SrcLocation
import Language.JavaScript.Pretty.Printer

-- EOF


50 changes: 46 additions & 4 deletions src/Language/JavaScript/Parser/AST.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ module Language.JavaScript.Parser.AST
, JSCommaList (..)
, JSCommaTrailingList (..)

-- Modules
, JSModuleItem (..)
, JSExportDeclaration (..)
, JSExportLocalSpecifier (..)

, binOpEq
, showStripped
) where
Expand All @@ -42,12 +47,34 @@ data JSAnnot


data JSAST
= JSAstProgram ![JSStatement] !JSAnnot -- ^source elements, tailing whitespace
= JSAstProgram ![JSStatement] !JSAnnot -- ^source elements, trailing whitespace
| JSAstModule ![JSModuleItem] !JSAnnot
| JSAstStatement !JSStatement !JSAnnot
| JSAstExpression !JSExpression !JSAnnot
| JSAstLiteral !JSExpression !JSAnnot
deriving (Data, Eq, Show, Typeable)

-- Shift AST
-- https://github.com/shapesecurity/shift-spec/blob/83498b92c436180cc0e2115b225a68c08f43c53e/spec.idl#L229-L234
data JSModuleItem
-- = JSImportDeclaration
= JSModuleExportDeclaration !JSAnnot !JSExportDeclaration -- ^export,decl
| JSModuleStatementListItem !JSStatement
deriving (Data, Eq, Show, Typeable)

data JSExportDeclaration
-- = JSExportAllFrom
-- | JSExportFrom
= JSExportLocals !JSAnnot !(JSCommaList JSExportLocalSpecifier) !JSAnnot !JSSemi -- ^lb, specifiers, rb, autosemi
| JSExport !JSStatement !JSSemi -- ^body, autosemi
-- | JSExportDefault
deriving (Data, Eq, Show, Typeable)

data JSExportLocalSpecifier
= JSExportLocalSpecifier !JSIdent -- ^ident
| JSExportLocalSpecifierAs !JSIdent !JSBinOp !JSIdent -- ^ident1, as, ident2
deriving (Data, Eq, Show, Typeable)

data JSStatement
= JSStatementBlock !JSAnnot ![JSStatement] !JSAnnot !JSSemi -- ^lbrace, stmts, rbrace, autosemi
| JSBreak !JSAnnot !JSIdent !JSSemi -- ^break,optional identifier, autosemi
Expand All @@ -71,7 +98,7 @@ data JSStatement
| JSSwitch !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSAnnot ![JSSwitchParts] !JSAnnot !JSSemi -- ^switch,lb,expr,rb,caseblock,autosemi
| JSThrow !JSAnnot !JSExpression !JSSemi -- ^throw val autosemi
| JSTry !JSAnnot !JSBlock ![JSTryCatch] !JSTryFinally -- ^try,block,catches,finally
| JSVariable !JSAnnot !(JSCommaList JSExpression) !JSSemi -- ^var|const, decl, autosemi
| JSVariable !JSAnnot !(JSCommaList JSExpression) !JSSemi -- ^var, decl, autosemi
| JSWhile !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSStatement -- ^while,lb,expr,rb,stmt
| JSWith !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSStatement !JSSemi -- ^with,lb,expr,rb,stmt list
deriving (Data, Eq, Show, Typeable)
Expand Down Expand Up @@ -112,6 +139,7 @@ data JSExpression

data JSBinOp
= JSBinOpAnd !JSAnnot
| JSBinOpAs !JSAnnot
| JSBinOpBitAnd !JSAnnot
| JSBinOpBitOr !JSAnnot
| JSBinOpBitXor !JSAnnot
Expand Down Expand Up @@ -233,11 +261,12 @@ data JSCommaTrailingList a
deriving (Data, Eq, Show, Typeable)

-- -----------------------------------------------------------------------------
-- | Show the AST elements stipped of their JSAnnot data.
-- | Show the AST elements stripped of their JSAnnot data.

-- Strip out the location info
showStripped :: JSAST -> String
showStripped (JSAstProgram xs _) = "JSAstProgram " ++ ss xs
showStripped (JSAstModule xs _) = "JSAstModule " ++ ss xs
showStripped (JSAstStatement s _) = "JSAstStatement (" ++ ss s ++ ")"
showStripped (JSAstExpression e _) = "JSAstExpression (" ++ ss e ++ ")"
showStripped (JSAstLiteral s _) = "JSAstLiteral (" ++ ss s ++ ")"
Expand All @@ -246,7 +275,6 @@ showStripped (JSAstLiteral s _) = "JSAstLiteral (" ++ ss s ++ ")"
class ShowStripped a where
ss :: a -> String


instance ShowStripped JSStatement where
ss (JSStatementBlock _ xs _ _) = "JSStatementBlock " ++ ss xs
ss (JSBreak _ JSIdentNone s) = "JSBreak" ++ commaIf (ss s)
Expand Down Expand Up @@ -308,6 +336,18 @@ instance ShowStripped JSExpression where
ss (JSVarInitExpression x1 x2) = "JSVarInitExpression (" ++ ss x1 ++ ") " ++ ss x2
ss (JSSpreadExpression _ x1) = "JSSpreadExpression (" ++ ss x1 ++ ")"

instance ShowStripped JSModuleItem where
ss (JSModuleExportDeclaration _ x1) = "JSModuleExportDeclaration (" ++ ss x1 ++ ")"
ss (JSModuleStatementListItem x1) = "JSModuleStatementListItem (" ++ ss x1 ++ ")"

instance ShowStripped JSExportDeclaration where
ss (JSExportLocals _ xs _ _) = "JSExportLocals (" ++ ss xs ++ ")"
ss (JSExport x1 _) = "JSExport (" ++ ss x1 ++ ")"

instance ShowStripped JSExportLocalSpecifier where
ss (JSExportLocalSpecifier x1) = "JSExportLocalSpecifier (" ++ ss x1 ++ ")"
ss (JSExportLocalSpecifierAs x1 _ x2) = "JSExportLocalSpecifierAs (" ++ ss x1 ++ "," ++ ss x2 ++ ")"

instance ShowStripped JSTryCatch where
ss (JSCatch _ _lb x1 _rb x3) = "JSCatch (" ++ ss x1 ++ "," ++ ss x3 ++ ")"
ss (JSCatchIf _ _lb x1 _ ex _rb x3) = "JSCatch (" ++ ss x1 ++ ") if " ++ ss ex ++ " (" ++ ss x3 ++ ")"
Expand Down Expand Up @@ -342,6 +382,7 @@ instance ShowStripped JSSwitchParts where

instance ShowStripped JSBinOp where
ss (JSBinOpAnd _) = "'&&'"
ss (JSBinOpAs _) = "'as'"
ss (JSBinOpBitAnd _) = "'&'"
ss (JSBinOpBitOr _) = "'|'"
ss (JSBinOpBitXor _) = "'^'"
Expand Down Expand Up @@ -437,6 +478,7 @@ commaIf xs = ',' : xs

deAnnot :: JSBinOp -> JSBinOp
deAnnot (JSBinOpAnd _) = JSBinOpAnd JSNoAnnot
deAnnot (JSBinOpAs _) = JSBinOpAs JSNoAnnot
deAnnot (JSBinOpBitAnd _) = JSBinOpBitAnd JSNoAnnot
deAnnot (JSBinOpBitOr _) = JSBinOpBitOr JSNoAnnot
deAnnot (JSBinOpBitXor _) = JSBinOpBitXor JSNoAnnot
Expand Down
105 changes: 95 additions & 10 deletions src/Language/JavaScript/Parser/Grammar7.y
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
{-# LANGUAGE BangPatterns #-}
module Language.JavaScript.Parser.Grammar7
( parseProgram
, parseStatement
, parseExpression
, parseLiteral
) where
( parseProgram
, parseModule
, parseStatement
, parseExpression
, parseLiteral
) where

import Data.Char
import Language.JavaScript.Parser.Lexer
Expand All @@ -18,8 +19,9 @@ import qualified Language.JavaScript.Parser.AST as AST

-- The name of the generated function to be exported from the module
%name parseProgram Program
%name parseModule Module
%name parseLiteral LiteralMain
%name parseExpression ExpressionMain
%name parseExpression ExpressionMain
%name parseStatement StatementMain

%tokentype { Token }
Expand Down Expand Up @@ -81,6 +83,7 @@ import qualified Language.JavaScript.Parser.AST as AST
'(' { LeftParenToken {} }
')' { RightParenToken {} }

'as' { AsToken {} }
'autosemi' { AutoSemiToken {} }
'break' { BreakToken {} }
'case' { CaseToken {} }
Expand All @@ -93,6 +96,7 @@ import qualified Language.JavaScript.Parser.AST as AST
'do' { DoToken {} }
'else' { ElseToken {} }
'enum' { EnumToken {} }
'export' { ExportToken {} }
'false' { FalseToken {} }
'finally' { FinallyToken {} }
'for' { ForToken {} }
Expand Down Expand Up @@ -244,6 +248,9 @@ Ge : '>=' { AST.JSBinOpGe (mkJSAnnot $1) }
Gt :: { AST.JSBinOp }
Gt : '>' { AST.JSBinOpGt (mkJSAnnot $1) }

As :: { AST.JSBinOp }
As : 'as' { AST.JSBinOpAs (mkJSAnnot $1) }

In :: { AST.JSBinOp }
In : 'in' { AST.JSBinOpIn (mkJSAnnot $1) }

Expand Down Expand Up @@ -305,6 +312,9 @@ Let : 'let' { mkJSAnnot $1 }
Const :: { AST.JSAnnot }
Const : 'const' { mkJSAnnot $1 }

Export :: { AST.JSAnnot }
Export : 'export' { mkJSAnnot $1 }

If :: { AST.JSAnnot }
If : 'if' { mkJSAnnot $1 }

Expand Down Expand Up @@ -426,6 +436,7 @@ Identifier : 'ident' { AST.JSIdentifier (mkJSAnnot $1) (tokenLiteral $1) }
-- TODO: make this include any reserved word too, including future ones
IdentifierName :: { AST.JSExpression }
IdentifierName : Identifier {$1}
| 'as' { AST.JSIdentifier (mkJSAnnot $1) "as" }
| 'break' { AST.JSIdentifier (mkJSAnnot $1) "break" }
| 'case' { AST.JSIdentifier (mkJSAnnot $1) "case" }
| 'catch' { AST.JSIdentifier (mkJSAnnot $1) "catch" }
Expand All @@ -437,6 +448,7 @@ IdentifierName : Identifier {$1}
| 'do' { AST.JSIdentifier (mkJSAnnot $1) "do" }
| 'else' { AST.JSIdentifier (mkJSAnnot $1) "else" }
| 'enum' { AST.JSIdentifier (mkJSAnnot $1) "enum" }
| 'export' { AST.JSIdentifier (mkJSAnnot $1) "export" }
| 'false' { AST.JSIdentifier (mkJSAnnot $1) "false" }
| 'finally' { AST.JSIdentifier (mkJSAnnot $1) "finally" }
| 'for' { AST.JSIdentifier (mkJSAnnot $1) "for" }
Expand Down Expand Up @@ -498,9 +510,9 @@ Elision : Comma { [AST.JSArrayComma $1] {- 'Elision1' -} }
-- { PropertyNameAndValueList }
-- { PropertyNameAndValueList , }
ObjectLiteral :: { AST.JSExpression }
ObjectLiteral : LBrace RBrace { AST.JSObjectLiteral $1 (AST.JSCTLNone AST.JSLNil) $2 {- 'ObjectLiteal1' -} }
| LBrace PropertyNameandValueList RBrace { AST.JSObjectLiteral $1 (AST.JSCTLNone $2) $3 {- 'ObjectLiteal2' -} }
| LBrace PropertyNameandValueList Comma RBrace { AST.JSObjectLiteral $1 (AST.JSCTLComma $2 $3) $4 {- 'ObjectLiteal3' -} }
ObjectLiteral : LBrace RBrace { AST.JSObjectLiteral $1 (AST.JSCTLNone AST.JSLNil) $2 {- 'ObjectLiteral1' -} }
| LBrace PropertyNameandValueList RBrace { AST.JSObjectLiteral $1 (AST.JSCTLNone $2) $3 {- 'ObjectLiteral2' -} }
| LBrace PropertyNameandValueList Comma RBrace { AST.JSObjectLiteral $1 (AST.JSCTLComma $2 $3) $4 {- 'ObjectLiteral3' -} }

-- <Property Name and Value List> ::= <Property Name> ':' <Assignment Expression>
-- | <Property Name and Value List> ',' <Property Name> ':' <Assignment Expression>
Expand Down Expand Up @@ -1108,6 +1120,11 @@ StatementOrBlock :: { AST.JSStatement }
StatementOrBlock : Block MaybeSemi { blockToStatement $1 $2 }
| Expression MaybeSemi { expressionToStatement $1 $2 }

-- StatementListItem :
-- Statement
-- Declaration
StatementListItem :: { AST.JSStatement }
StatementListItem : Statement { $1 }

NamedFunctionExpression :: { AST.JSExpression }
NamedFunctionExpression : Function Identifier LParen RParen FunctionBody
Expand Down Expand Up @@ -1145,6 +1162,75 @@ Program :: { AST.JSAST }
Program : StatementList Eof { AST.JSAstProgram $1 $2 {- 'Program1' -} }
| Eof { AST.JSAstProgram [] $1 {- 'Program2' -} }

-- Module : See 15.2
-- ModuleBody[opt]
--
-- ModuleBody :
-- ModuleItemList
Module :: { AST.JSAST }
Module : ModuleItemList Eof { AST.JSAstModule $1 $2 {- 'Module1' -} }
| Eof { AST.JSAstModule [] $1 {- 'Module2' -} }

-- ModuleItemList :
-- ModuleItem
-- ModuleItemList ModuleItem
ModuleItemList :: { [AST.JSModuleItem] }
ModuleItemList : ModuleItem { [$1] {- 'ModuleItemList1' -} }
| ModuleItemList ModuleItem { ($1++[$2]) {- 'ModuleItemList2' -} }

-- ModuleItem :
-- ImportDeclaration
-- ExportDeclaration
-- StatementListItem
ModuleItem :: { AST.JSModuleItem }
ModuleItem : Export ExportDeclaration
{ AST.JSModuleExportDeclaration $1 $2 {- 'ModuleItem1' -} }
| StatementListItem
{ AST.JSModuleStatementListItem $1 {- 'ModuleItem2' -} }

-- ExportDeclaration : See 15.2.3
-- [ ] export * FromClause ;
-- [ ] export ExportClause FromClause ;
-- [x] export ExportClause ;
-- [x] export VariableStatement
-- [ ] export Declaration
-- [ ] export default HoistableDeclaration[Default]
-- [ ] export default ClassDeclaration[Default]
-- [ ] export default [lookahead ∉ { function, class }] AssignmentExpression[In] ;
ExportDeclaration :: { AST.JSExportDeclaration }
ExportDeclaration : ExportClause AutoSemi
{ $1 {- 'ExportDeclaration1' -} }
| VariableStatement AutoSemi
{ AST.JSExport $1 $2 {- 'ExportDeclaration2' -} }

-- ExportClause :
-- { }
-- { ExportsList }
-- { ExportsList , }
ExportClause :: { AST.JSExportDeclaration }
ExportClause : LBrace RBrace AutoSemi
{ AST.JSExportLocals $1 AST.JSLNil $2 $3 {- 'ExportClause1' -} }
| LBrace ExportsList RBrace AutoSemi
{ AST.JSExportLocals $1 $2 $3 $4 {- 'ExportClause2' -} }

-- ExportsList :
-- ExportSpecifier
-- ExportsList , ExportSpecifier
ExportsList :: { AST.JSCommaList AST.JSExportLocalSpecifier }
ExportsList : ExportSpecifier
{ AST.JSLOne $1 {- 'ExportsList1' -} }
| ExportsList Comma ExportSpecifier
{ AST.JSLCons $1 $2 $3 {- 'ExportsList2' -} }

-- ExportSpecifier :
-- IdentifierName
-- IdentifierName as IdentifierName
ExportSpecifier :: { AST.JSExportLocalSpecifier }
ExportSpecifier : IdentifierName
{ AST.JSExportLocalSpecifier (identName $1) {- 'ExportSpecifier1' -} }
| IdentifierName As IdentifierName
{ AST.JSExportLocalSpecifierAs (identName $1) $2 (identName $3) {- 'ExportSpecifier2' -} }

-- For debugging/other entry points
LiteralMain :: { AST.JSAST }
LiteralMain : Literal Eof { AST.JSAstLiteral $1 $2 {- 'LiteralMain' -} }
Expand All @@ -1155,7 +1241,6 @@ ExpressionMain : Expression Eof { AST.JSAstExpression $1 $2 {- 'ExpressionMa
StatementMain :: { AST.JSAST }
StatementMain : StatementNoEmpty Eof { AST.JSAstStatement $1 $2 {- 'StatementMain' -} }


{

-- Need this type while build the AST, but is not actually part of the AST.
Expand Down
5 changes: 3 additions & 2 deletions src/Language/JavaScript/Parser/Lexer.x
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,8 @@ keywords = Map.fromList keywordNames

keywordNames :: [(String, TokenPosn -> String -> [CommentAnnotation] -> Token)]
keywordNames =
[ ( "break", BreakToken )
[ ( "as", AsToken )
, ( "break", BreakToken )
, ( "case", CaseToken )
, ( "catch", CatchToken )

Expand All @@ -517,6 +518,7 @@ keywordNames =
, ( "else", ElseToken )

, ( "enum", EnumToken ) -- not a keyword, nominally a future reserved word, but actually in use
, ( "export", ExportToken )

, ( "false", FalseToken ) -- boolean literal

Expand Down Expand Up @@ -563,7 +565,6 @@ keywordNames =
-- ( "code", FutureToken ) **** not any more
-- ( "const", FutureToken ) **** an actual token, used in productions
-- enum **** an actual token, used in productions
, ( "export", FutureToken )
, ( "extends", FutureToken )

, ( "import", FutureToken )
Expand Down
Loading