module Tests.StatementParser ( testStatementParser ) where import Test.Hspec import Language.JavaScript.Parser import Language.JavaScript.Parser.Grammar5 import Language.JavaScript.Parser.Parser testStatementParser :: Spec testStatementParser = describe "Parse statements:" $ do it "simple" $ do testStmt "x" `shouldBe` "Right (JSAstStatement (JSIdentifier 'x'))" testStmt "null" `shouldBe` "Right (JSAstStatement (JSLiteral 'null'))" testStmt "true?1:2" `shouldBe` "Right (JSAstStatement (JSExpressionTernary (JSLiteral 'true',JSDecimal '1',JSDecimal '2')))" it "block" $ 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 [JSStatementBlock []]))" testStmt "{{{}}}" `shouldBe` "Right (JSAstStatement (JSStatementBlock [JSStatementBlock [JSStatementBlock []]]))" it "if" $ testStmt "if (1) {}" `shouldBe` "Right (JSAstStatement (JSIf (JSDecimal '1') (JSStatementBlock [])))" it "if/else" $ do testStmt "if (1) {} else {}" `shouldBe` "Right (JSAstStatement (JSIfElse (JSDecimal '1') (JSStatementBlock []) (JSStatementBlock [])))" testStmt "if (1) x=1; else {}" `shouldBe` "Right (JSAstStatement (JSIfElse (JSDecimal '1') (JSOpAssign ('=',JSIdentifier 'x',JSDecimal '1'),JSSemicolon) (JSStatementBlock [])))" testStmt " if (1);else break" `shouldBe` "Right (JSAstStatement (JSIfElse (JSDecimal '1') (JSEmptyStatement) (JSBreak)))" it "while" $ testStmt "while(true);" `shouldBe` "Right (JSAstStatement (JSWhile (JSLiteral 'true') (JSEmptyStatement)))" it "do/while" $ do testStmt "do {x=1} while (true);" `shouldBe` "Right (JSAstStatement (JSDoWhile (JSStatementBlock [JSOpAssign ('=',JSIdentifier 'x',JSDecimal '1')]) (JSLiteral 'true') (JSSemicolon)))" testStmt "do x=x+1;while(x<4);" `shouldBe` "Right (JSAstStatement (JSDoWhile (JSOpAssign ('=',JSIdentifier 'x',JSExpressionBinary ('+',JSIdentifier 'x',JSDecimal '1')),JSSemicolon) (JSExpressionBinary ('<',JSIdentifier 'x',JSDecimal '4')) (JSSemicolon)))" it "for" $ do testStmt "for(;;);" `shouldBe` "Right (JSAstStatement (JSFor () () () (JSEmptyStatement)))" testStmt "for(x=1;x<10;x++);" `shouldBe` "Right (JSAstStatement (JSFor (JSOpAssign ('=',JSIdentifier 'x',JSDecimal '1')) (JSExpressionBinary ('<',JSIdentifier 'x',JSDecimal '10')) (JSExpressionPostfix ('++',JSIdentifier 'x')) (JSEmptyStatement)))" testStmt "for(var x;;);" `shouldBe` "Right (JSAstStatement (JSForVar (JSVarInitExpression (JSIdentifier 'x') ) () () (JSEmptyStatement)))" testStmt "for(var x=1;;);" `shouldBe` "Right (JSAstStatement (JSForVar (JSVarInitExpression (JSIdentifier 'x') [JSDecimal '1']) () () (JSEmptyStatement)))" testStmt "for(var x;y;z){}" `shouldBe` "Right (JSAstStatement (JSForVar (JSVarInitExpression (JSIdentifier 'x') ) (JSIdentifier 'y') (JSIdentifier 'z') (JSStatementBlock [])))" testStmt "for(x in 5){}" `shouldBe` "Right (JSAstStatement (JSForIn JSIdentifier 'x' (JSDecimal '5') (JSStatementBlock [])))" testStmt "for(var x in 5){}" `shouldBe` "Right (JSAstStatement (JSForVarIn (JSVarInitExpression (JSIdentifier 'x') ) (JSDecimal '5') (JSStatementBlock [])))" it "variable/constant 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'])))" it "break" $ do testStmt "break;" `shouldBe` "Right (JSAstStatement (JSBreak,JSSemicolon))" testStmt "break x;" `shouldBe` "Right (JSAstStatement (JSBreak 'x',JSSemicolon))" testStmt "{break}" `shouldBe` "Right (JSAstStatement (JSStatementBlock [JSBreak]))" it "continue" $ do testStmt "continue;" `shouldBe` "Right (JSAstStatement (JSContinue,JSSemicolon))" testStmt "continue x;" `shouldBe` "Right (JSAstStatement (JSContinue 'x',JSSemicolon))" testStmt "{continue}" `shouldBe` "Right (JSAstStatement (JSStatementBlock [JSContinue]))" it "return" $ do testStmt "return;" `shouldBe` "Right (JSAstStatement (JSReturn JSSemicolon))" testStmt "return x;" `shouldBe` "Right (JSAstStatement (JSReturn JSIdentifier 'x' JSSemicolon))" testStmt "return 123;" `shouldBe` "Right (JSAstStatement (JSReturn JSDecimal '123' JSSemicolon))" testStmt "{return}" `shouldBe` "Right (JSAstStatement (JSStatementBlock [JSReturn ]))" it "with" $ testStmt "with (x) {};" `shouldBe` "Right (JSAstStatement (JSWith (JSIdentifier 'x') (JSStatementBlock [])))" it "assign" $ testStmt "var z = x[i] / y;" `shouldBe` "Right (JSAstStatement (JSVariable (JSVarInitExpression (JSIdentifier 'z') [JSExpressionBinary ('/',JSMemberSquare (JSIdentifier 'x',JSIdentifier 'i'),JSIdentifier 'y')])))" it "label" $ testStmt "abc:x=1" `shouldBe` "Right (JSAstStatement (JSLabelled (JSIdentifier 'abc') (JSOpAssign ('=',JSIdentifier 'x',JSDecimal '1'))))" it "throw" $ testStmt "throw 1" `shouldBe` "Right (JSAstStatement (JSThrow (JSDecimal '1')))" it "switch" $ do testStmt "switch (x) {}" `shouldBe` "Right (JSAstStatement (JSSwitch (JSIdentifier 'x') []))" testStmt "switch (x) {case 1:break;}" `shouldBe` "Right (JSAstStatement (JSSwitch (JSIdentifier 'x') [JSCase (JSDecimal '1') ([JSBreak,JSSemicolon])]))" testStmt "switch (x) {case 0:\ncase 1:break;}" `shouldBe` "Right (JSAstStatement (JSSwitch (JSIdentifier 'x') [JSCase (JSDecimal '0') ([]),JSCase (JSDecimal '1') ([JSBreak,JSSemicolon])]))" testStmt "switch (x) {default:break;}" `shouldBe` "Right (JSAstStatement (JSSwitch (JSIdentifier 'x') [JSDefault ([JSBreak,JSSemicolon])]))" testStmt "switch (x) {default:\ncase 1:break;}" `shouldBe` "Right (JSAstStatement (JSSwitch (JSIdentifier 'x') [JSDefault ([]),JSCase (JSDecimal '1') ([JSBreak,JSSemicolon])]))" it "try/cathc/finally" $ do testStmt "try{}catch(a){}" `shouldBe` "Right (JSAstStatement (JSTry (JSBlock [],[JSCatch (JSIdentifier 'a',JSBlock [])],JSFinally ())))" testStmt "try{}finally{}" `shouldBe` "Right (JSAstStatement (JSTry (JSBlock [],[],JSFinally (JSBlock []))))" testStmt "try{}catch(a){}finally{}" `shouldBe` "Right (JSAstStatement (JSTry (JSBlock [],[JSCatch (JSIdentifier 'a',JSBlock [])],JSFinally (JSBlock []))))" testStmt "try{}catch(a){}catch(b){}finally{}" `shouldBe` "Right (JSAstStatement (JSTry (JSBlock [],[JSCatch (JSIdentifier 'a',JSBlock []),JSCatch (JSIdentifier 'b',JSBlock [])],JSFinally (JSBlock []))))" testStmt "try{}catch(a){}catch(b){}" `shouldBe` "Right (JSAstStatement (JSTry (JSBlock [],[JSCatch (JSIdentifier 'a',JSBlock []),JSCatch (JSIdentifier 'b',JSBlock [])],JSFinally ())))" testStmt "try{}catch(a if true){}catch(b){}" `shouldBe` "Right (JSAstStatement (JSTry (JSBlock [],[JSCatch (JSIdentifier 'a') if JSLiteral 'true' (JSBlock []),JSCatch (JSIdentifier 'b',JSBlock [])],JSFinally ())))" testStmt :: String -> String testStmt str = showStrippedMaybe (parseUsing parseStatement str "src")