Skip to content

Fix arrow function body parsing #117

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

Open
wants to merge 3 commits into
base: new-ast
Choose a base branch
from
Open
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
12 changes: 11 additions & 1 deletion src/Language/JavaScript/Parser/AST.hs
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
module Language.JavaScript.Parser.AST
( JSExpression (..)
, JSAnnot (..)
, JSConciseBody(..)
, JSBinOp (..)
, JSUnaryOp (..)
, JSSemi (..)
@@ -188,7 +189,7 @@ data JSExpression
| JSExpressionParen !JSAnnot !JSExpression !JSAnnot -- ^lb,expression,rb
| JSExpressionPostfix !JSExpression !JSUnaryOp -- ^expression, operator
| JSExpressionTernary !JSExpression !JSAnnot !JSExpression !JSAnnot !JSExpression -- ^cond, ?, trueval, :, falseval
| JSArrowExpression !JSArrowParameterList !JSAnnot !JSStatement -- ^parameter list,arrow,block`
| JSArrowExpression !JSArrowParameterList !JSAnnot !JSConciseBody -- ^parameter list,arrow,body`
| JSFunctionExpression !JSAnnot !JSIdent !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSBlock -- ^fn,name,lb, parameter list,rb,block`
| JSGeneratorExpression !JSAnnot !JSAnnot !JSIdent !JSAnnot !(JSCommaList JSExpression) !JSAnnot !JSBlock -- ^fn,*,name,lb, parameter list,rb,block`
| JSMemberDot !JSExpression !JSAnnot !JSExpression -- ^firstpart, dot, name
@@ -205,6 +206,11 @@ data JSExpression
| JSYieldFromExpression !JSAnnot !JSAnnot !JSExpression -- ^yield, *, expr
deriving (Data, Eq, Show, Typeable)

data JSConciseBody
= JSConciseFunctionBody !JSBlock
| JSConciseExpressionBody !JSExpression
deriving (Data, Eq, Show, Typeable)

data JSArrowParameterList
= JSUnparenthesizedArrowParameter !JSIdent
| JSParenthesizedArrowParameterList !JSAnnot !(JSCommaList JSExpression) !JSAnnot
@@ -455,6 +461,10 @@ instance ShowStripped JSArrowParameterList where
ss (JSUnparenthesizedArrowParameter x) = ss x
ss (JSParenthesizedArrowParameterList _ xs _) = ss xs

instance ShowStripped JSConciseBody where
ss (JSConciseFunctionBody b) = ss b
ss (JSConciseExpressionBody e) = ss e

instance ShowStripped JSModuleItem where
ss (JSModuleExportDeclaration _ x1) = "JSModuleExportDeclaration (" ++ ss x1 ++ ")"
ss (JSModuleImportDeclaration _ x1) = "JSModuleImportDeclaration (" ++ ss x1 ++ ")"
16 changes: 12 additions & 4 deletions src/Language/JavaScript/Parser/Grammar7.y
Original file line number Diff line number Diff line change
@@ -1238,18 +1238,26 @@ FunctionExpression : ArrowFunctionExpression { $1 {- 'ArrowFunctionExpressio
| LambdaExpression { $1 {- 'FunctionExpression1' -} }
| NamedFunctionExpression { $1 {- 'FunctionExpression2' -} }

-- ArrowFunctionExpression :
-- ( ArrowParameterList ) => ConciseBody See clause 14.2
ArrowFunctionExpression :: { AST.JSExpression }
ArrowFunctionExpression : ArrowParameterList Arrow StatementOrBlock
ArrowFunctionExpression : ArrowParameterList Arrow ConciseBody
{ AST.JSArrowExpression $1 $2 $3 }

ArrowParameterList :: { AST.JSArrowParameterList }
ArrowParameterList : PrimaryExpression {%^ toArrowParameterList $1 }
| LParen RParen
{ AST.JSParenthesizedArrowParameterList $1 AST.JSLNil $2 }

StatementOrBlock :: { AST.JSStatement }
StatementOrBlock : Block MaybeSemi { blockToStatement $1 $2 }
| Expression MaybeSemi { expressionToStatement $1 $2 }
-- ConciseBody :
-- { FunctionBody }
-- ExpressionBody
ConciseBody :: { AST.JSConciseBody }
ConciseBody : FunctionBody { AST.JSConciseFunctionBody $1 }
| ExpressionBody { AST.JSConciseExpressionBody $1 }

ExpressionBody :: { AST.JSExpression }
ExpressionBody : AssignmentExpression { $1 }

-- StatementListItem :
-- Statement
4 changes: 4 additions & 0 deletions src/Language/JavaScript/Pretty/Printer.hs
Original file line number Diff line number Diff line change
@@ -104,6 +104,10 @@ instance RenderJS JSExpression where
instance RenderJS JSArrowParameterList where
(|>) pacc (JSUnparenthesizedArrowParameter p) = pacc |> p
(|>) pacc (JSParenthesizedArrowParameterList lb ps rb) = pacc |> lb |> "(" |> ps |> ")" |> rb

instance RenderJS JSConciseBody where
(|>) pacc (JSConciseExpressionBody e) = pacc |> e
(|>) pacc (JSConciseFunctionBody b) = pacc |> b
-- -----------------------------------------------------------------------------
-- Need an instance of RenderJS for every component of every JSExpression or JSAnnot
-- constuctor.
6 changes: 5 additions & 1 deletion src/Language/JavaScript/Process/Minify.hs
Original file line number Diff line number Diff line change
@@ -156,7 +156,7 @@ instance MinifyJS JSExpression where

-- Non-Terminals
fix _ (JSArrayLiteral _ xs _) = JSArrayLiteral emptyAnnot (map fixEmpty xs) emptyAnnot
fix a (JSArrowExpression ps _ ss) = JSArrowExpression (fix a ps) emptyAnnot (fixStmt emptyAnnot noSemi ss)
fix a (JSArrowExpression ps _ fb) = JSArrowExpression (fix a ps) emptyAnnot (fixEmpty fb)
fix a (JSAssignExpression lhs op rhs) = JSAssignExpression (fix a lhs) (fixEmpty op) (fixEmpty rhs)
fix a (JSAwaitExpression _ ex) = JSAwaitExpression a (fixSpace ex)
fix a (JSCallExpression ex _ xs _) = JSCallExpression (fix a ex) emptyAnnot (fixEmpty xs) emptyAnnot
@@ -187,6 +187,10 @@ instance MinifyJS JSArrowParameterList where
fix _ (JSUnparenthesizedArrowParameter p) = JSUnparenthesizedArrowParameter (fixEmpty p)
fix _ (JSParenthesizedArrowParameterList _ ps _) = JSParenthesizedArrowParameterList emptyAnnot (fixEmpty ps) emptyAnnot

instance MinifyJS JSConciseBody where
fix _ (JSConciseExpressionBody e) = JSConciseExpressionBody (fixEmpty e)
fix _ (JSConciseFunctionBody b) = JSConciseFunctionBody (fixEmpty b)

fixVarList :: JSCommaList JSExpression -> JSCommaList JSExpression
fixVarList (JSLCons h _ v) = JSLCons (fixVarList h) emptyAnnot (fixEmpty v)
fixVarList (JSLOne a) = JSLOne (fixSpace a)
10 changes: 6 additions & 4 deletions test/Test/Language/Javascript/ExpressionParser.hs
Original file line number Diff line number Diff line change
@@ -137,14 +137,16 @@ testExpressionParser = describe "Parse expressions:" $ do
testExpr "function([a,b]){}" `shouldBe` "Right (JSAstExpression (JSFunctionExpression '' (JSArrayLiteral [JSIdentifier 'a',JSComma,JSIdentifier 'b']) (JSBlock [])))"
testExpr "function([a,...b]){}" `shouldBe` "Right (JSAstExpression (JSFunctionExpression '' (JSArrayLiteral [JSIdentifier 'a',JSComma,JSSpreadExpression (JSIdentifier 'b')]) (JSBlock [])))"
testExpr "function({a,b}){}" `shouldBe` "Right (JSAstExpression (JSFunctionExpression '' (JSObjectLiteral [JSPropertyIdentRef 'a',JSPropertyIdentRef 'b']) (JSBlock [])))"
testExpr "a => {}" `shouldBe` "Right (JSAstExpression (JSArrowExpression (JSIdentifier 'a') => JSStatementBlock []))"
testExpr "(a) => { a + 2 }" `shouldBe` "Right (JSAstExpression (JSArrowExpression ((JSIdentifier 'a')) => JSStatementBlock [JSExpressionBinary ('+',JSIdentifier 'a',JSDecimal '2')]))"
testExpr "(a, b) => {}" `shouldBe` "Right (JSAstExpression (JSArrowExpression ((JSIdentifier 'a',JSIdentifier 'b')) => JSStatementBlock []))"
testExpr "a => {}" `shouldBe` "Right (JSAstExpression (JSArrowExpression (JSIdentifier 'a') => JSBlock []))"
testExpr "(a) => { a + 2 }" `shouldBe` "Right (JSAstExpression (JSArrowExpression ((JSIdentifier 'a')) => JSBlock [JSExpressionBinary ('+',JSIdentifier 'a',JSDecimal '2')]))"
testExpr "(a, b) => {}" `shouldBe` "Right (JSAstExpression (JSArrowExpression ((JSIdentifier 'a',JSIdentifier 'b')) => JSBlock []))"
testExpr "(a, b) => a + b" `shouldBe` "Right (JSAstExpression (JSArrowExpression ((JSIdentifier 'a',JSIdentifier 'b')) => JSExpressionBinary ('+',JSIdentifier 'a',JSIdentifier 'b')))"
testExpr "() => { 42 }" `shouldBe` "Right (JSAstExpression (JSArrowExpression (()) => JSStatementBlock [JSDecimal '42']))"
testExpr "() => { 42 }" `shouldBe` "Right (JSAstExpression (JSArrowExpression (()) => JSBlock [JSDecimal '42']))"
testExpr "(a, ...b) => b" `shouldBe` "Right (JSAstExpression (JSArrowExpression ((JSIdentifier 'a',JSSpreadExpression (JSIdentifier 'b'))) => JSIdentifier 'b'))"
testExpr "(a,b=1) => a + b" `shouldBe` "Right (JSAstExpression (JSArrowExpression ((JSIdentifier 'a',JSOpAssign ('=',JSIdentifier 'b',JSDecimal '1'))) => JSExpressionBinary ('+',JSIdentifier 'a',JSIdentifier 'b')))"
testExpr "([a,b]) => a + b" `shouldBe` "Right (JSAstExpression (JSArrowExpression ((JSArrayLiteral [JSIdentifier 'a',JSComma,JSIdentifier 'b'])) => JSExpressionBinary ('+',JSIdentifier 'a',JSIdentifier 'b')))"
testExpr "{f:()=>{},y:2}" `shouldBe` "Right (JSAstExpression (JSObjectLiteral [JSPropertyNameandValue (JSIdentifier 'f') [JSArrowExpression (()) => JSBlock []],JSPropertyNameandValue (JSIdentifier 'y') [JSDecimal '2']]))"
testExpr "{f:x=>x,y:2}" `shouldBe` "Right (JSAstExpression (JSObjectLiteral [JSPropertyNameandValue (JSIdentifier 'f') [JSArrowExpression (JSIdentifier 'x') => JSIdentifier 'x'],JSPropertyNameandValue (JSIdentifier 'y') [JSDecimal '2']]))"

it "generator expression" $ do
testExpr "function*(){}" `shouldBe` "Right (JSAstExpression (JSGeneratorExpression '' () (JSBlock [])))"
4 changes: 2 additions & 2 deletions test/Test/Language/Javascript/Minify.hs
Original file line number Diff line number Diff line change
@@ -117,9 +117,9 @@ testMinifyExpr = describe "Minify expressions:" $ do

minifyExpr "a => {}" `shouldBe` "a=>{}"
minifyExpr "(a) => {}" `shouldBe` "(a)=>{}"
minifyExpr "( a ) => { a + 2 }" `shouldBe` "(a)=>a+2"
minifyExpr "( a ) => { a + 2 }" `shouldBe` "(a)=>{a+2}"
minifyExpr "(a, b) => a + b" `shouldBe` "(a,b)=>a+b"
minifyExpr "() => { 42 }" `shouldBe` "()=>42"
minifyExpr "() => { 42 }" `shouldBe` "()=>{42}"
minifyExpr "(a, ...b) => b" `shouldBe` "(a,...b)=>b"
minifyExpr "(a = 1, b = 2) => a + b" `shouldBe` "(a=1,b=2)=>a+b"
minifyExpr "( [ a , b ] ) => a + b" `shouldBe` "([a,b])=>a+b"
2 changes: 2 additions & 0 deletions test/Test/Language/Javascript/StatementParser.hs
Original file line number Diff line number Diff line change
@@ -21,6 +21,8 @@ testStatementParser = describe "Parse statements:" $ do
testStmt "{}" `shouldBe` "Right (JSAstStatement (JSStatementBlock []))"
testStmt "{x=1}" `shouldBe` "Right (JSAstStatement (JSStatementBlock [JSOpAssign ('=',JSIdentifier 'x',JSDecimal '1')]))"
testStmt "{x=1;y=2}" `shouldBe` "Right (JSAstStatement (JSStatementBlock [JSOpAssign ('=',JSIdentifier 'x',JSDecimal '1'),JSSemicolon,JSOpAssign ('=',JSIdentifier 'y',JSDecimal '2')]))"
testStmt "{()=>{};``}" `shouldBe` "Right (JSAstStatement (JSStatementBlock [JSArrowExpression (()) => JSBlock [],JSSemicolon,JSTemplateLiteral ((),'``',[])]))"
testStmt "{x=>x;``}" `shouldBe` "Right (JSAstStatement (JSStatementBlock [JSArrowExpression (JSIdentifier 'x') => JSIdentifier 'x',JSSemicolon,JSTemplateLiteral ((),'``',[])]))"
testStmt "{{}}" `shouldBe` "Right (JSAstStatement (JSStatementBlock [JSStatementBlock []]))"
testStmt "{{{}}}" `shouldBe` "Right (JSAstStatement (JSStatementBlock [JSStatementBlock [JSStatementBlock []]]))"