Skip to content

Add support for classes, super keyword #108

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

Merged
merged 2 commits into from
Sep 29, 2019
Merged
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
26 changes: 26 additions & 0 deletions src/Language/JavaScript/Parser/AST.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ module Language.JavaScript.Parser.AST
, JSCommaTrailingList (..)
, JSArrowParameterList (..)
, JSTemplatePart (..)
, JSClassHeritage (..)
, JSClassElement (..)

-- Modules
, JSModuleItem (..)
Expand Down Expand Up @@ -128,6 +130,7 @@ data JSStatement
= JSStatementBlock !JSAnnot ![JSStatement] !JSAnnot !JSSemi -- ^lbrace, stmts, rbrace, autosemi
| JSBreak !JSAnnot !JSIdent !JSSemi -- ^break,optional identifier, autosemi
| JSLet !JSAnnot !(JSCommaList JSExpression) !JSSemi -- ^const, decl, autosemi
| JSClass !JSAnnot !JSIdent !JSClassHeritage !JSAnnot ![JSClassElement] !JSAnnot !JSSemi -- ^class, name, optional extends clause, lb, body, rb, autosemi
| JSConstant !JSAnnot !(JSCommaList JSExpression) !JSSemi -- ^const, decl, autosemi
| JSContinue !JSAnnot !JSIdent !JSSemi -- ^continue, optional identifier,autosemi
| JSDoWhile !JSAnnot !JSStatement !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSSemi -- ^do,stmt,while,lb,expr,rb,autosemi
Expand Down Expand Up @@ -177,6 +180,7 @@ data JSExpression
| JSCallExpression !JSExpression !JSAnnot !(JSCommaList JSExpression) !JSAnnot -- ^expr, bl, args, rb
| JSCallExpressionDot !JSExpression !JSAnnot !JSExpression -- ^expr, dot, expr
| JSCallExpressionSquare !JSExpression !JSAnnot !JSExpression !JSAnnot -- ^expr, [, expr, ]
| JSClassExpression !JSAnnot !JSIdent !JSClassHeritage !JSAnnot ![JSClassElement] !JSAnnot -- ^class, optional identifier, optional extends clause, lb, body, rb
| JSCommaExpression !JSExpression !JSAnnot !JSExpression -- ^expression components
| JSExpressionBinary !JSExpression !JSBinOp !JSExpression -- ^lhs, op, rhs
| JSExpressionParen !JSAnnot !JSExpression !JSAnnot -- ^lb,expression,rb
Expand Down Expand Up @@ -339,6 +343,17 @@ data JSTemplatePart
= JSTemplatePart !JSExpression !JSAnnot !String -- ^expr, rb, suffix
deriving (Data, Eq, Show, Typeable)

data JSClassHeritage
= JSExtends !JSAnnot !JSExpression
| JSExtendsNone
deriving (Data, Eq, Show, Typeable)

data JSClassElement
= JSClassInstanceMethod !JSMethodDefinition
| JSClassStaticMethod !JSAnnot !JSMethodDefinition
| JSClassSemi !JSAnnot
deriving (Data, Eq, Show, Typeable)

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

Expand All @@ -358,6 +373,7 @@ instance ShowStripped JSStatement where
ss (JSStatementBlock _ xs _ _) = "JSStatementBlock " ++ ss xs
ss (JSBreak _ JSIdentNone s) = "JSBreak" ++ commaIf (ss s)
ss (JSBreak _ (JSIdentName _ n) s) = "JSBreak " ++ singleQuote n ++ commaIf (ss s)
ss (JSClass _ n h _lb xs _rb _) = "JSClass " ++ ssid n ++ " (" ++ ss h ++ ") " ++ ss xs
ss (JSContinue _ JSIdentNone s) = "JSContinue" ++ commaIf (ss s)
ss (JSContinue _ (JSIdentName _ n) s) = "JSContinue " ++ singleQuote n ++ commaIf (ss s)
ss (JSConstant _ xs _as) = "JSConstant " ++ ss xs
Expand Down Expand Up @@ -399,6 +415,7 @@ instance ShowStripped JSExpression where
ss (JSCallExpression ex _ xs _) = "JSCallExpression ("++ ss ex ++ ",JSArguments " ++ ss xs ++ ")"
ss (JSCallExpressionDot ex _os xs) = "JSCallExpressionDot (" ++ ss ex ++ "," ++ ss xs ++ ")"
ss (JSCallExpressionSquare ex _os xs _cs) = "JSCallExpressionSquare (" ++ ss ex ++ "," ++ ss xs ++ ")"
ss (JSClassExpression _ n h _lb xs _rb) = "JSClassExpression " ++ ssid n ++ " (" ++ ss h ++ ") " ++ ss xs
ss (JSDecimal _ s) = "JSDecimal " ++ singleQuote s
ss (JSCommaExpression l _ r) = "JSExpression [" ++ ss l ++ "," ++ ss r ++ "]"
ss (JSExpressionBinary x2 op x3) = "JSExpressionBinary (" ++ ss op ++ "," ++ ss x2 ++ "," ++ ss x3 ++ ")"
Expand Down Expand Up @@ -580,6 +597,15 @@ instance ShowStripped JSArrayElement where
instance ShowStripped JSTemplatePart where
ss (JSTemplatePart e _ s) = "(" ++ ss e ++ "," ++ singleQuote s ++ ")"

instance ShowStripped JSClassHeritage where
ss JSExtendsNone = ""
ss (JSExtends _ x) = ss x

instance ShowStripped JSClassElement where
ss (JSClassInstanceMethod m) = ss m
ss (JSClassStaticMethod _ m) = "JSClassStaticMethod (" ++ ss m ++ ")"
ss (JSClassSemi _) = "JSClassSemi"

instance ShowStripped a => ShowStripped (JSCommaList a) where
ss xs = "(" ++ commaJoin (map ss $ fromCommaList xs) ++ ")"

Expand Down
68 changes: 67 additions & 1 deletion src/Language/JavaScript/Parser/Grammar7.y
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import qualified Language.JavaScript.Parser.AST as AST
'break' { BreakToken {} }
'case' { CaseToken {} }
'catch' { CatchToken {} }
'class' { ClassToken {} }
'const' { ConstToken {} }
'continue' { ContinueToken {} }
'debugger' { DebuggerToken {} }
Expand All @@ -98,6 +99,7 @@ import qualified Language.JavaScript.Parser.AST as AST
'else' { ElseToken {} }
'enum' { EnumToken {} }
'export' { ExportToken {} }
'extends' { ExtendsToken {} }
'false' { FalseToken {} }
'finally' { FinallyToken {} }
'for' { ForToken {} }
Expand All @@ -114,6 +116,8 @@ import qualified Language.JavaScript.Parser.AST as AST
'of' { OfToken {} }
'return' { ReturnToken {} }
'set' { SetToken {} }
'static' { StaticToken {} }
'super' { SuperToken {} }
'switch' { SwitchToken {} }
'this' { ThisToken {} }
'throw' { ThrowToken {} }
Expand Down Expand Up @@ -330,6 +334,7 @@ IdentifierName : Identifier {$1}
| 'break' { AST.JSIdentifier (mkJSAnnot $1) "break" }
| 'case' { AST.JSIdentifier (mkJSAnnot $1) "case" }
| 'catch' { AST.JSIdentifier (mkJSAnnot $1) "catch" }
| 'class' { AST.JSIdentifier (mkJSAnnot $1) "class" }
| 'const' { AST.JSIdentifier (mkJSAnnot $1) "const" }
| 'continue' { AST.JSIdentifier (mkJSAnnot $1) "continue" }
| 'debugger' { AST.JSIdentifier (mkJSAnnot $1) "debugger" }
Expand All @@ -339,6 +344,7 @@ IdentifierName : Identifier {$1}
| 'else' { AST.JSIdentifier (mkJSAnnot $1) "else" }
| 'enum' { AST.JSIdentifier (mkJSAnnot $1) "enum" }
| 'export' { AST.JSIdentifier (mkJSAnnot $1) "export" }
| 'extends' { AST.JSIdentifier (mkJSAnnot $1) "extends" }
| 'false' { AST.JSIdentifier (mkJSAnnot $1) "false" }
| 'finally' { AST.JSIdentifier (mkJSAnnot $1) "finally" }
| 'for' { AST.JSIdentifier (mkJSAnnot $1) "for" }
Expand All @@ -351,6 +357,8 @@ IdentifierName : Identifier {$1}
| 'null' { AST.JSIdentifier (mkJSAnnot $1) "null" }
| 'of' { AST.JSIdentifier (mkJSAnnot $1) "of" }
| 'return' { AST.JSIdentifier (mkJSAnnot $1) "return" }
| 'static' { AST.JSIdentifier (mkJSAnnot $1) "static" }
| 'super' { AST.JSIdentifier (mkJSAnnot $1) "super" }
| 'switch' { AST.JSIdentifier (mkJSAnnot $1) "switch" }
| 'this' { AST.JSIdentifier (mkJSAnnot $1) "this" }
| 'throw' { AST.JSIdentifier (mkJSAnnot $1) "throw" }
Expand Down Expand Up @@ -435,6 +443,18 @@ Function : 'function' { mkJSAnnot $1 {- 'Function' -} }
New :: { AST.JSAnnot }
New : 'new' { mkJSAnnot $1 }

Class :: { AST.JSAnnot }
Class : 'class' { mkJSAnnot $1 }

Extends :: { AST.JSAnnot }
Extends : 'extends' { mkJSAnnot $1 }

Static :: { AST.JSAnnot }
Static : 'static' { mkJSAnnot $1 }

Super :: { AST.JSExpression }
Super : 'super' { AST.JSLiteral (mkJSAnnot $1) "super" }


Eof :: { AST.JSAnnot }
Eof : 'tail' { mkJSAnnot $1 {- 'Eof' -} }
Expand Down Expand Up @@ -486,6 +506,7 @@ PrimaryExpression : 'this' { AST.JSLiteral (mkJSAnnot $1) "thi
| Literal { $1 {- 'PrimaryExpression2' -} }
| ArrayLiteral { $1 {- 'PrimaryExpression3' -} }
| ObjectLiteral { $1 {- 'PrimaryExpression4' -} }
| ClassExpression { $1 }
| GeneratorExpression { $1 }
| TemplateLiteral { mkJSTemplateLiteral Nothing $1 {- 'PrimaryExpression6' -} }
| LParen Expression RParen { AST.JSExpressionParen $1 $2 $3 }
Expand Down Expand Up @@ -625,6 +646,8 @@ MemberExpression : PrimaryExpression { $1 {- 'MemberExpression1' -} }
| MemberExpression LSquare Expression RSquare { AST.JSMemberSquare $1 $2 $3 $4 {- 'MemberExpression3' -} }
| MemberExpression Dot IdentifierName { AST.JSMemberDot $1 $2 $3 {- 'MemberExpression4' -} }
| MemberExpression TemplateLiteral { mkJSTemplateLiteral (Just $1) $2 }
| Super LSquare Expression RSquare { AST.JSMemberSquare $1 $2 $3 $4 }
| Super Dot IdentifierName { AST.JSMemberDot $1 $2 $3 }
| New MemberExpression Arguments { mkJSMemberNew $1 $2 $3 {- 'MemberExpression5' -} }

-- NewExpression : See 11.2
Expand All @@ -642,6 +665,8 @@ NewExpression : MemberExpression { $1 {- 'NewExpressio
CallExpression :: { AST.JSExpression }
CallExpression : MemberExpression Arguments
{ mkJSMemberExpression $1 $2 {- 'CallExpression1' -} }
| Super Arguments
{ mkJSCallExpression $1 $2 }
| CallExpression Arguments
{ mkJSCallExpression $1 $2 {- 'CallExpression2' -} }
| CallExpression LSquare Expression RSquare
Expand Down Expand Up @@ -1272,6 +1297,44 @@ FormalParameterList : AssignmentExpression { AST.JSLOn
FunctionBody :: { AST.JSBlock }
FunctionBody : Block { $1 {- 'FunctionBody1' -} }

-- ClassDeclaration :
-- class BindingIdentifier ClassTail
-- class ClassTail
-- ClassExpression :
-- class BindingIdentifieropt ClassTail
-- ClassTail :
-- ClassHeritageopt { ClassBodyopt }
ClassDeclaration :: { AST.JSStatement }
ClassDeclaration : Class Identifier ClassHeritage LBrace ClassBody RBrace { AST.JSClass $1 (identName $2) $3 $4 $5 $6 AST.JSSemiAuto }

ClassExpression :: { AST.JSExpression }
ClassExpression : Class Identifier ClassHeritage LBrace ClassBody RBrace { AST.JSClassExpression $1 (identName $2) $3 $4 $5 $6 }
| Class ClassHeritage LBrace ClassBody RBrace { AST.JSClassExpression $1 AST.JSIdentNone $2 $3 $4 $5 }

-- ClassHeritage :
-- extends LeftHandSideExpression
ClassHeritage :: { AST.JSClassHeritage }
ClassHeritage : Extends LeftHandSideExpression { AST.JSExtends $1 $2 }
| { AST.JSExtendsNone }

-- ClassBody :
-- ClassElementList
-- ClassElementList :
-- ClassElement
-- ClassElementList ClassElement
ClassBody :: { [AST.JSClassElement] }
ClassBody : { [] }
| ClassBody ClassElement { $1 ++ [$2] }

-- ClassElement :
-- MethodDefinition
-- static MethodDefinition
-- ;
ClassElement :: { AST.JSClassElement }
ClassElement : MethodDefinition { AST.JSClassInstanceMethod $1 }
| Static MethodDefinition { AST.JSClassStaticMethod $1 $2 }
| Semi { AST.JSClassSemi $1 }

-- Program : See clause 14
-- SourceElementsopt

Expand Down Expand Up @@ -1357,7 +1420,7 @@ ImportSpecifier : IdentifierName
-- [ ] export Declaration
-- [ ] Declaration :
-- [ ] HoistableDeclaration
-- [ ] ClassDeclaration
-- [x] ClassDeclaration
-- [x] LexicalDeclaration
-- [ ] HoistableDeclaration :
-- [x] FunctionDeclaration
Expand All @@ -1378,6 +1441,8 @@ ExportDeclaration : ExportClause FromClause AutoSemi
{ AST.JSExport $1 $2 {- 'ExportDeclaration4' -} }
| GeneratorDeclaration AutoSemi
{ AST.JSExport $1 $2 {- 'ExportDeclaration5' -} }
| ClassDeclaration AutoSemi
{ AST.JSExport $1 $2 {- 'ExportDeclaration6' -} }

-- ExportClause :
-- { }
Expand Down Expand Up @@ -1431,6 +1496,7 @@ expressionToStatement (AST.JSFunctionExpression a b@(AST.JSIdentName{}) c d e f)
expressionToStatement (AST.JSGeneratorExpression a b c@(AST.JSIdentName{}) d e f g) s = AST.JSGenerator a b c d e f g s
expressionToStatement (AST.JSAssignExpression lhs op rhs) s = AST.JSAssignStatement lhs op rhs s
expressionToStatement (AST.JSMemberExpression e l a r) s = AST.JSMethodCall e l a r s
expressionToStatement (AST.JSClassExpression a b@(AST.JSIdentName{}) c d e f) s = AST.JSClass a b c d e f s
expressionToStatement exp s = AST.JSExpressionStatement exp s


Expand Down
12 changes: 8 additions & 4 deletions src/Language/JavaScript/Parser/Lexer.x
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ keywordNames =
, ( "case", CaseToken )
, ( "catch", CatchToken )

, ( "class", ClassToken )
, ( "const", ConstToken ) -- not a keyword, nominally a future reserved word, but actually in use

, ( "continue", ContinueToken )
Expand All @@ -544,6 +545,7 @@ keywordNames =

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

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

Expand All @@ -562,6 +564,8 @@ keywordNames =

, ( "of", OfToken )
, ( "return", ReturnToken )
, ( "static", StaticToken )
, ( "super", SuperToken )
, ( "switch", SwitchToken )
, ( "this", ThisToken )
, ( "throw", ThrowToken )
Expand Down Expand Up @@ -591,12 +595,12 @@ keywordNames =


-- Future Reserved Words
, ( "class", FutureToken )
-- ( "class", FutureToken ) **** an actual token, used in productions
-- ( "code", FutureToken ) **** not any more
-- ( "const", FutureToken ) **** an actual token, used in productions
-- enum **** an actual token, used in productions
, ( "extends", FutureToken )
, ( "super", FutureToken )
-- ( "extends", FutureToken ) **** an actual token, used in productions
-- ( "super", FutureToken ) **** an actual token, used in productions


-- Strict mode FutureReservedWords
Expand All @@ -611,7 +615,7 @@ keywordNames =
, ( "private", FutureToken )
, ( "protected", FutureToken )
, ( "public", FutureToken )
, ( "static", FutureToken )
-- ( "static", FutureToken ) **** an actual token, used in productions
-- ( "strict", FutureToken ) *** not any more
-- ( "yield", FutureToken) **** an actual token, used in productions
]
Expand Down
4 changes: 4 additions & 0 deletions src/Language/JavaScript/Parser/Token.hs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ data Token
| BreakToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| CaseToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| CatchToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| ClassToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| ConstToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| LetToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| ContinueToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
Expand All @@ -68,6 +69,7 @@ data Token
| DoToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| ElseToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| EnumToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| ExtendsToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| FalseToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| FinallyToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| ForToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
Expand All @@ -80,6 +82,8 @@ data Token
| NullToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| OfToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| ReturnToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| StaticToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| SuperToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| SwitchToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| ThisToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
| ThrowToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] }
Expand Down
14 changes: 14 additions & 0 deletions src/Language/JavaScript/Pretty/Printer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ instance RenderJS JSExpression where
(|>) pacc (JSCallExpression ex lb xs rb) = pacc |> ex |> lb |> "(" |> xs |> rb |> ")"
(|>) pacc (JSCallExpressionDot ex os xs) = pacc |> ex |> os |> "." |> xs
(|>) pacc (JSCallExpressionSquare ex als xs ars) = pacc |> ex |> als |> "[" |> xs |> ars |> "]"
(|>) pacc (JSClassExpression annot n h lb xs rb) = pacc |> annot |> "class" |> n |> h |> lb |> "{" |> xs |> rb |> "}"
(|>) pacc (JSCommaExpression le c re) = pacc |> le |> c |> "," |> re
(|>) pacc (JSExpressionBinary lhs op rhs) = pacc |> lhs |> op |> rhs
(|>) pacc (JSExpressionParen alp e arp) = pacc |> alp |> "(" |> e |> arp |> ")"
Expand Down Expand Up @@ -224,6 +225,7 @@ instance RenderJS [JSSwitchParts] where
instance RenderJS JSStatement where
(|>) pacc (JSStatementBlock alb blk arb s) = pacc |> alb |> "{" |> blk |> arb |> "}" |> s
(|>) pacc (JSBreak annot mi s) = pacc |> annot |> "break" |> mi |> s
(|>) pacc (JSClass annot n h lb xs rb s) = pacc |> annot |> "class" |> n |> h |> lb |> "{" |> xs |> rb |> "}" |> s
(|>) pacc (JSContinue annot mi s) = pacc |> annot |> "continue" |> mi |> s
(|>) pacc (JSConstant annot xs s) = pacc |> annot |> "const" |> xs |> s
(|>) pacc (JSDoWhile ad x1 aw alb x2 arb x3) = pacc |> ad |> "do" |> x1 |> aw |> "while" |> alb |> "(" |> x2 |> arb |> ")" |> x3
Expand Down Expand Up @@ -362,4 +364,16 @@ instance RenderJS [JSTemplatePart] where
instance RenderJS JSTemplatePart where
(|>) pacc (JSTemplatePart e a s) = pacc |> e |> a |> s

instance RenderJS JSClassHeritage where
(|>) pacc (JSExtends a e) = pacc |> a |> "extends" |> e
(|>) pacc JSExtendsNone = pacc

instance RenderJS [JSClassElement] where
(|>) = foldl' (|>)

instance RenderJS JSClassElement where
(|>) pacc (JSClassInstanceMethod m) = pacc |> m
(|>) pacc (JSClassStaticMethod a m) = pacc |> a |> "static" |> m
(|>) pacc (JSClassSemi a) = pacc |> a |> ";"

-- EOF
Loading