From 36d9544d888e036d6037bef5f9f34299391f4590 Mon Sep 17 00:00:00 2001 From: Bruno Dias Date: Sun, 7 Jan 2018 22:07:11 -0300 Subject: [PATCH 1/2] Parse let statements --- src/Language/JavaScript/Parser/AST.hs | 2 ++ src/Language/JavaScript/Parser/Grammar7.y | 8 +++++++- src/Language/JavaScript/Parser/Lexer.x | 2 +- src/Language/JavaScript/Parser/Token.hs | 1 + test/Test/Language/Javascript/StatementParser.hs | 3 ++- 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Language/JavaScript/Parser/AST.hs b/src/Language/JavaScript/Parser/AST.hs index 6164719..5bd6d81 100644 --- a/src/Language/JavaScript/Parser/AST.hs +++ b/src/Language/JavaScript/Parser/AST.hs @@ -51,6 +51,7 @@ data JSAST 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 | 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 @@ -251,6 +252,7 @@ instance ShowStripped JSStatement where 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 + ss (JSLet _ xs _as) = "JSLet " ++ ss xs ss (JSDoWhile _d x1 _w _lb x2 _rb x3) = "JSDoWhile (" ++ ss x1 ++ ") (" ++ ss x2 ++ ") (" ++ ss x3 ++ ")" ss (JSFor _ _lb x1s _s1 x2s _s2 x3s _rb x4) = "JSFor " ++ ss x1s ++ " " ++ ss x2s ++ " " ++ ss x3s ++ " (" ++ ss x4 ++ ")" ss (JSForIn _ _lb x1s _i x2 _rb x3) = "JSForIn " ++ ss x1s ++ " (" ++ ss x2 ++ ") (" ++ ss x3 ++ ")" diff --git a/src/Language/JavaScript/Parser/Grammar7.y b/src/Language/JavaScript/Parser/Grammar7.y index 9ce110d..3861f19 100644 --- a/src/Language/JavaScript/Parser/Grammar7.y +++ b/src/Language/JavaScript/Parser/Grammar7.y @@ -99,6 +99,7 @@ import qualified Language.JavaScript.Parser.AST as AST 'if' { IfToken {} } 'in' { InToken {} } 'instanceof' { InstanceofToken {} } + 'let' { LetToken {} } 'new' { NewToken {} } 'null' { NullToken {} } 'return' { ReturnToken {} } @@ -290,6 +291,9 @@ OpAssign : '*=' { AST.JSTimesAssign (mkJSAnnot $1) } Var :: { AST.JSAnnot } Var : 'var' { mkJSAnnot $1 } +Let :: { AST.JSAnnot } +Let : 'let' { mkJSAnnot $1 } + Const :: { AST.JSAnnot } Const : 'const' { mkJSAnnot $1 } @@ -432,6 +436,7 @@ IdentifierName : Identifier {$1} | 'if' { AST.JSIdentifier (mkJSAnnot $1) "if" } | 'in' { AST.JSIdentifier (mkJSAnnot $1) "in" } | 'instanceof' { AST.JSIdentifier (mkJSAnnot $1) "instanceof" } + | 'let' { AST.JSIdentifier (mkJSAnnot $1) "let" } | 'new' { AST.JSIdentifier (mkJSAnnot $1) "new" } | 'null' { AST.JSIdentifier (mkJSAnnot $1) "null" } | 'return' { AST.JSIdentifier (mkJSAnnot $1) "return" } @@ -894,7 +899,8 @@ StatementList : Statement { [$1] {- 'StatementList1' -} } -- var VariableDeclarationList ; VariableStatement :: { AST.JSStatement } VariableStatement : Var VariableDeclarationList MaybeSemi { AST.JSVariable $1 $2 $3 {- 'VariableStatement1' -} } - | Const VariableDeclarationList MaybeSemi { AST.JSConstant $1 $2 $3 {- 'VariableStatement2' -} } + | Let VariableDeclarationList MaybeSemi { AST.JSLet $1 $2 $3 {- 'VariableStatement2' -} } + | Const VariableDeclarationList MaybeSemi { AST.JSConstant $1 $2 $3 {- 'VariableStatement3' -} } -- VariableDeclarationList : See 12.2 -- VariableDeclaration diff --git a/src/Language/JavaScript/Parser/Lexer.x b/src/Language/JavaScript/Parser/Lexer.x index d7e21e1..915ab7b 100644 --- a/src/Language/JavaScript/Parser/Lexer.x +++ b/src/Language/JavaScript/Parser/Lexer.x @@ -519,6 +519,7 @@ keywordNames = , ( "if", IfToken ) , ( "in", InToken ) , ( "instanceof", InstanceofToken ) + , ( "let", LetToken ) , ( "new", NewToken ) , ( "null", NullToken ) -- null literal @@ -565,7 +566,6 @@ keywordNames = -- Strict mode FutureReservedWords , ( "implements", FutureToken ) , ( "interface", FutureToken ) - , ( "let", FutureToken ) -- ( "mode", FutureToken ) **** not any more -- ( "of", FutureToken ) **** not any more -- ( "one", FutureToken ) **** not any more diff --git a/src/Language/JavaScript/Parser/Token.hs b/src/Language/JavaScript/Parser/Token.hs index cc360b4..29a9a62 100644 --- a/src/Language/JavaScript/Parser/Token.hs +++ b/src/Language/JavaScript/Parser/Token.hs @@ -60,6 +60,7 @@ data Token | CaseToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] } | CatchToken { 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] } | DebuggerToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] } | DefaultToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] } diff --git a/test/Test/Language/Javascript/StatementParser.hs b/test/Test/Language/Javascript/StatementParser.hs index 2eb8a7d..e27addf 100644 --- a/test/Test/Language/Javascript/StatementParser.hs +++ b/test/Test/Language/Javascript/StatementParser.hs @@ -50,9 +50,10 @@ testStatementParser = describe "Parse statements:" $ do testStmt "for(var x in 5){}" `shouldBe` "Right (JSAstStatement (JSForVarIn (JSVarInitExpression (JSIdentifier 'x') ) (JSDecimal '5') (JSStatementBlock [])))" - it "variable/constant declaration" $ do + it "variable/constant/let declaration" $ do testStmt "var x=1;" `shouldBe` "Right (JSAstStatement (JSVariable (JSVarInitExpression (JSIdentifier 'x') [JSDecimal '1'])))" testStmt "const x=1,y=2;" `shouldBe` "Right (JSAstStatement (JSConstant (JSVarInitExpression (JSIdentifier 'x') [JSDecimal '1'],JSVarInitExpression (JSIdentifier 'y') [JSDecimal '2'])))" + testStmt "let x=1,y=2;" `shouldBe` "Right (JSAstStatement (JSLet (JSVarInitExpression (JSIdentifier 'x') [JSDecimal '1'],JSVarInitExpression (JSIdentifier 'y') [JSDecimal '2'])))" it "break" $ do testStmt "break;" `shouldBe` "Right (JSAstStatement (JSBreak,JSSemicolon))" From 37168f28f58da32d4a322077cb55a50d04c25ddf Mon Sep 17 00:00:00 2001 From: Erik de Castro Lopo Date: Sun, 17 Jun 2018 12:16:56 +1000 Subject: [PATCH 2/2] Finalize support for the let statement Was missing pretty-print and minifying support as wll as tests. --- src/Language/JavaScript/Pretty/Printer.hs | 2 ++ src/Language/JavaScript/Process/Minify.hs | 2 ++ test/Test/Language/Javascript/Minify.hs | 2 ++ test/Test/Language/Javascript/RoundTrip.hs | 1 + 4 files changed, 7 insertions(+) diff --git a/src/Language/JavaScript/Pretty/Printer.hs b/src/Language/JavaScript/Pretty/Printer.hs index fd1fbe6..a8641d9 100644 --- a/src/Language/JavaScript/Pretty/Printer.hs +++ b/src/Language/JavaScript/Pretty/Printer.hs @@ -236,6 +236,8 @@ instance RenderJS JSStatement where (|>) pacc (JSWhile annot alp x1 arp x2) = pacc |> annot |> "while" |> alp |> "(" |> x1 |> arp |> ")" |> x2 (|>) pacc (JSWith annot alp x1 arp x s) = pacc |> annot |> "with" |> alp |> "(" |> x1 |> arp |> ")" |> x |> s + (|>) pacc (JSLet annot xs s) = pacc |> annot |> "let" |> xs |> s + instance RenderJS [JSStatement] where (|>) = foldl' (|>) diff --git a/src/Language/JavaScript/Process/Minify.hs b/src/Language/JavaScript/Process/Minify.hs index 2aa3b6e..f911c0f 100644 --- a/src/Language/JavaScript/Process/Minify.hs +++ b/src/Language/JavaScript/Process/Minify.hs @@ -64,6 +64,8 @@ fixStmt a s (JSVariable _ ss _) = JSVariable a (fixVarList ss) s fixStmt a s (JSWhile _ _ e _ st) = JSWhile a emptyAnnot (fixEmpty e) emptyAnnot (fixStmt a s st) fixStmt a s (JSWith _ _ e _ st _) = JSWith a emptyAnnot (fixEmpty e) emptyAnnot (fixStmtE noSemi st) s +fixStmt a s (JSLet _ xs _) = JSLet a (fixVarList xs) s + fixIfElseBlock :: JSAnnot -> JSSemi -> JSStatement -> JSStatement fixIfElseBlock _ _ (JSStatementBlock _ [] _ _) = JSEmptyStatement emptyAnnot fixIfElseBlock a s st = fixStmt a s st diff --git a/test/Test/Language/Javascript/Minify.hs b/test/Test/Language/Javascript/Minify.hs index 36969e4..6342743 100644 --- a/test/Test/Language/Javascript/Minify.hs +++ b/test/Test/Language/Javascript/Minify.hs @@ -206,6 +206,8 @@ testMinifyStmt = describe "Minify statements:" $ do minifyStmt " var b ; " `shouldBe` "var b" minifyStmt " var c = 1 ; " `shouldBe` "var c=1" minifyStmt " var d = 1, x = 2 ; " `shouldBe` "var d=1,x=2" + minifyStmt " let c = 1 ; " `shouldBe` "let c=1" + minifyStmt " let d = 1, x = 2 ; " `shouldBe` "let d=1,x=2" it "string concatenation" $ minifyStmt " f (\"ab\"+\"cd\") " `shouldBe` "f('abcd')" diff --git a/test/Test/Language/Javascript/RoundTrip.hs b/test/Test/Language/Javascript/RoundTrip.hs index e2e7d39..272f86a 100644 --- a/test/Test/Language/Javascript/RoundTrip.hs +++ b/test/Test/Language/Javascript/RoundTrip.hs @@ -90,6 +90,7 @@ testRoundTrip = describe "Roundtrip:" $ do testRT "switch (x) {case 0:\ncase 1:break;}" testRT "switch (x) {default:break;}" testRT "switch (x) {default:\ncase 1:break;}" + testRT "var x=1;let y=2;" testRT :: String -> Expectation