packages feed

language-dart-0.1.0.0: test/Spec.hs

import Test.Hspec

import Language.Dart.Pretty
import Language.Dart.Syntax

intExp :: Integer -> Expression
intExp = Literal' . IntegerLiteral

ident :: String -> Identifier
ident = SimpleIdentifier' . SimpleIdentifier

identExp :: String -> Expression
identExp = Identifier' . ident

tyName :: String -> TypeName
tyName name = TypeName (ident name) Nothing

strType = tyName "String"
numType = tyName "num"

formal :: TypeName -> String -> FormalParameter
formal ty p = NormalFormalParameter' $
  SimpleFormalParameter Nothing [] (FVTType ty) (SimpleIdentifier p)

posFormal :: TypeName -> String -> FormalParameter
posFormal ty p = DefaultFormalParameter parameter Positional Nothing
  where parameter = (SimpleFormalParameter Nothing [] (FVTType ty) (SimpleIdentifier p))

var :: String -> FormalParameter
var p = NormalFormalParameter' $
  SimpleFormalParameter Nothing [] FVTVar (SimpleIdentifier p)

strLit :: String -> StringLiteral
strLit = SingleStringLiteral' .  SimpleStringLiteral

strLitExp :: String -> Expression
strLitExp = Literal' . StringLiteral' . strLit

nullExp :: Expression
nullExp = Literal' NullLiteral

funStmt :: String -> Statement
funStmt name = ExpressionStatement (
                 InvocationExpression (
                   MethodInvocation Nothing
                                    (SimpleIdentifier name)
                                    Nothing
                                    (ArgumentList [])))

funStmt1 :: String -> Expression -> Statement
funStmt1 name arg1 = ExpressionStatement (
                       InvocationExpression (
                         MethodInvocation Nothing
                                          (SimpleIdentifier name)
                                          Nothing
                                          (ArgumentList [arg1])))

prettyPrintSpec :: Spec
prettyPrintSpec =
  describe "prettyPrint" $ do
    it "preserves operator precedence" $ do
      let l = BinaryExpression (intExp 1) "+" (intExp 2)
          r = BinaryExpression (intExp 3) "-" (intExp 2)
          e = BinaryExpression l "*" r
      prettyPrint e `shouldBe` "(1 + 2) * (3 - 2)"

    it "prints an import directive" $ do
      let libraryUri = strLit "package:analyzer/src/dart/ast/ast.dart"
          as = SimpleIdentifier "a"
          imp = ImportDirective Nothing
                                []
                                libraryUri
                                []
                                False
                                (Just as)
                                [ ShowCombinator [SimpleIdentifier "TypeName"]
                                , ShowCombinator [SimpleIdentifier "ArgumentList"]
                                ]
      prettyPrint imp `shouldBe` "import \"package:analyzer/src/dart/ast/ast.dart\" as a show TypeName, show ArgumentList;"

    it "prints a function declaration" $ do
      let formals = FormalParameterList [formal numType "aNumber"]
          arg = Literal' . StringLiteral' . SingleStringLiteral' $
                  StringInterpolation [ InterpolationString "The number is "
                                      , InterpolationExpression (identExp "aNumber")
                                      , InterpolationString "."]
          body = BlockFunctionBody Sync
                                   (Block [ ExpressionStatement (
                                             InvocationExpression (
                                               MethodInvocation Nothing
                                                                (SimpleIdentifier "print")
                                                                Nothing
                                                                (ArgumentList [arg])))])
          funExp = FunctionExpression Nothing formals body
          funDecl = FunctionDeclaration Nothing [] False Nothing Empty (SimpleIdentifier "printNumber") funExp
      prettyPrint funDecl `shouldBe` "printNumber(num aNumber)\n\
                                     \{\n\
                                     \  print(\"The number is ${aNumber}.\");\n\
                                     \}"

    it "prints an optional position parameter" $ do
      let formals = FormalParameterList [formal strType "from", formal strType "msg", posFormal strType "device"]
          str1 = Literal' . StringLiteral' . SingleStringLiteral' $
                  StringInterpolation [ InterpolationExpression (identExp "from")
                                      , InterpolationString " says "
                                      , InterpolationExpression (identExp "msg")
                                      ]
          str2 = Literal' . StringLiteral' . SingleStringLiteral' $
                  StringInterpolation [ InterpolationExpression (identExp "result")
                                      , InterpolationString " with a "
                                      , InterpolationExpression (identExp "device")
                                      ]
          body = BlockFunctionBody Sync
                                   (Block [ VariableDeclarationStatement (
                                              VariableDeclarationList Nothing
                                                                      []
                                                                      FCVTVar
                                                                      [VariableDeclaration (SimpleIdentifier "result") (Just str1)])
                                          , IfStatement (BinaryExpression (identExp "device") "!=" nullExp)
                                                        (Block' (Block [ ExpressionStatement (
                                                                          AssignmentExpression (identExp "result") "=" str2)]))
                                                        Nothing
                                          , ReturnStatement (Just (identExp "result"))
                                          ])
          funExp = FunctionExpression Nothing formals body
          funDecl = FunctionDeclaration Nothing [] False (Just strType) Empty (SimpleIdentifier "say") funExp
      prettyPrint funDecl `shouldBe` "String say(String from, String msg, [String device])\n\
                                     \{\n\
                                     \  var result = \"${from} says ${msg}\";\n\
                                     \  if (device != null)\n\
                                     \  {\n\
                                     \    result = \"${result} with a ${device}\";\n\
                                     \  }\n\
                                     \  return result;\n\
                                     \}"

    it "prints an expression function" $ do
      let body = ExpressionFunctionBody False (
                  ConditionalExpression (BinaryExpression (identExp "msg") "==" nullExp)
                                        (InvocationExpression
                                          (MethodInvocation (Just SuperExpression)
                                                            (SimpleIdentifier "toString")
                                                            Nothing
                                                            (ArgumentList [])))
                                        (identExp "msg"))

          funExp = FunctionExpression Nothing (FormalParameterList []) body
          funDecl = FunctionDeclaration Nothing [] False (Just strType) Empty (SimpleIdentifier "toString") funExp
      prettyPrint funDecl `shouldBe` "String toString() => msg == null ? super.toString() : msg;"

    it "prints an enum" $ do
      let enumConstDecls = [ EnumConstantDeclaration Nothing [] (SimpleIdentifier "red")
                           , EnumConstantDeclaration Nothing [] (SimpleIdentifier "green")
                           , EnumConstantDeclaration Nothing [] (SimpleIdentifier "blue")
                           ]
          enumDecl = EnumDeclaration Nothing [] (SimpleIdentifier "Color") enumConstDecls
      prettyPrint enumDecl `shouldBe` "enum Color\n\
                                      \{\n\
                                      \  red,\n\
                                      \  green,\n\
                                      \  blue\n\
                                      \}"

    it "prints a for loop" $ do
      let variableList = VariableDeclarationList Nothing
                                                 []
                                                 FCVTVar
                                                 [VariableDeclaration (SimpleIdentifier "i") (Just (intExp 0))]
          condition = BinaryExpression (identExp "i") "<" (intExp 5)
          updater = PostfixExpression (identExp "i") "++"
          body = (Block' (Block [ ExpressionStatement (
                                    InvocationExpression (
                                      MethodInvocation (Just (identExp "message"))
                                                       (SimpleIdentifier "write")
                                                       Nothing
                                                       (ArgumentList [strLitExp "!"])))]))
          forLoop = ForStatement (Just variableList)
                                 Nothing
                                 (Just condition)
                                 [updater]
                                 body
      prettyPrint forLoop `shouldBe` "for (var i = 0; i < 5; i++)\n\
                                     \{\n\
                                     \  message.write(\"!\");\n\
                                     \}"

    it "prints a list literal" $ do
      let litExp = Literal' . TypedLiteral $ ListLiteral False Nothing [intExp 0, intExp 1, intExp 2]
      prettyPrint litExp `shouldBe` "[0, 1, 2]"

    it "prints parameterized types" $ do
      let consName = ConstructorName (TypeName (SimpleIdentifier' (SimpleIdentifier "Map"))
                                               (Just (TypeArgumentList [tyName "int", tyName "View"])))
                                     Nothing
      let exp = InstanceCreationExpression NCNew consName (ArgumentList [])
      prettyPrint exp `shouldBe` "new Map<int, View>()"

    it "prints a switch statement" $ do
      let switchMembers = [ SwitchCase [] (strLitExp "CLOSED") [funStmt "executeClosed", (BreakStatement Nothing)]
                          , SwitchCase [] (strLitExp "PENDING") [funStmt "executePending", (BreakStatement Nothing)]
                          , SwitchCase [] (strLitExp "APPROVED") [funStmt "executeApproved", (BreakStatement Nothing)]
                          , SwitchCase [] (strLitExp "DENIED") [funStmt "executeDenied", (BreakStatement Nothing)]
                          , SwitchCase [] (strLitExp "OPEN") [funStmt "executeOpen", (BreakStatement Nothing)]
                          , SwitchDefault [] [funStmt "executeUnknown"]
                          ]
          switch = SwitchStatement (identExp "command") switchMembers
      prettyPrint switch `shouldBe` "switch (command)\n\
                                    \{\n\
                                    \  case \"CLOSED\":\n\
                                    \    executeClosed();\n\
                                    \    break;\n\
                                    \  case \"PENDING\":\n\
                                    \    executePending();\n\
                                    \    break;\n\
                                    \  case \"APPROVED\":\n\
                                    \    executeApproved();\n\
                                    \    break;\n\
                                    \  case \"DENIED\":\n\
                                    \    executeDenied();\n\
                                    \    break;\n\
                                    \  case \"OPEN\":\n\
                                    \    executeOpen();\n\
                                    \    break;\n\
                                    \  default:\n\
                                    \    executeUnknown();\n\
                                    \}"

    it "prints a try statement" $ do
      let arg = Literal' . StringLiteral' . SingleStringLiteral' $
                  StringInterpolation [ InterpolationString "Error: "
                                      , InterpolationExpression (identExp "e")
                                      ]
          try = TryStatement (Block [funStmt "breedMoreLlamas"])
                             [CatchClause Nothing (SimpleIdentifier "e") Nothing (Block [funStmt1 "print" arg])]
                             (Just (Block [funStmt "cleanLlamaStalls"]))
      prettyPrint try `shouldBe` "try\n\
                                 \{\n\
                                 \  breedMoreLlamas();\n\
                                 \}\n\
                                 \catch (e)\n\
                                 \{\n\
                                 \  print(\"Error: ${e}\");\n\
                                 \}\n\
                                 \finally\n\
                                 \{\n\
                                 \  cleanLlamaStalls();\n\
                                 \}"

    it "prints a constructor" $ do
      let varDeclList ty name = VariableDeclarationList Nothing
                                                        []
                                                        (FCVTType (tyName ty))
                                                        [VariableDeclaration (SimpleIdentifier name) Nothing]

          returnType = SimpleIdentifier' (SimpleIdentifier "Point")
          consFormals1 = FormalParameterList [ NormalFormalParameter' (FieldFormalParameter Nothing [] Nothing True (SimpleIdentifier "x"))
                                             , NormalFormalParameter' (FieldFormalParameter Nothing [] Nothing True (SimpleIdentifier "y"))
                                             ]
          consFormals2 = FormalParameterList [ NormalFormalParameter' (
                                                 FieldFormalParameter Nothing
                                                                      []
                                                                      (Just (FCVTType (tyName "Map")))
                                                                      False
                                                                      (SimpleIdentifier "json"))
                                             ]
          methodFormals = FormalParameterList [ NormalFormalParameter' (
                                                  FieldFormalParameter Nothing
                                                                       []
                                                                       (Just (FCVTType (tyName "Point")))
                                                                       False
                                                                       (SimpleIdentifier "other"))
                                              ]
          consBody = BlockFunctionBody Sync
                                   (Block [ ExpressionStatement (
                                              AssignmentExpression (identExp "x")
                                                                   "="
                                                                   (IndexExpressionForTarget (identExp "json") (strLitExp "x")))
                                          , ExpressionStatement (
                                              AssignmentExpression (identExp "y")
                                                                   "="
                                                                   (IndexExpressionForTarget (identExp "json") (strLitExp "y")))
                                          ])
          exp1 = BinaryExpression (identExp "x") "-" (PropertyAccess (identExp "other") (SimpleIdentifier "x"))
          exp2 = BinaryExpression (identExp "y") "-" (PropertyAccess (identExp "other") (SimpleIdentifier "y"))
          methodBody = BlockFunctionBody Sync
                                   (Block [ VariableDeclarationStatement (
                                              VariableDeclarationList Nothing
                                                                      []
                                                                      FCVTVar
                                                                      [VariableDeclaration (SimpleIdentifier "dx") (Just exp1)])
                                          , VariableDeclarationStatement (
                                              VariableDeclarationList Nothing
                                                                      []
                                                                      FCVTVar
                                                                      [VariableDeclaration (SimpleIdentifier "dy") (Just exp2)])
                                          , ReturnStatement (Just (
                                              InvocationExpression (
                                                MethodInvocation Nothing
                                                                 (SimpleIdentifier "sqrt")
                                                                 Nothing
                                                                 (ArgumentList [BinaryExpression (BinaryExpression (identExp "dx") "*" (identExp "dx"))
                                                                                                 "+"
                                                                                                 (BinaryExpression (identExp "dy") "*" (identExp "dy"))]))))
                                          ])
          classMembers = [ FieldDeclaration Nothing [] False (varDeclList "num" "x")
                         , FieldDeclaration Nothing [] False (varDeclList "num" "y")
                         , ConstructorDeclaration Nothing [] False False False returnType Nothing consFormals1 [] Nothing Nothing
                         , ConstructorDeclaration Nothing [] False False False returnType (Just (SimpleIdentifier "fromJson")) consFormals2 [] Nothing (Just consBody)
                         , MethodDeclaration Nothing [] False Nothing (Just (tyName "num")) Empty False (SimpleIdentifier "distanceTo") Nothing (Just methodFormals) methodBody
                         ]
          clazz = ClassDeclaration Nothing
                                   []
                                   False
                                   (SimpleIdentifier "Point")
                                   Nothing
                                   Nothing
                                   Nothing
                                   Nothing
                                   classMembers
      prettyPrint clazz `shouldBe` "class Point\n\
                                   \{\n\
                                   \  num x;\n\
                                   \  num y;\n\
                                   \  Point(this.x, this.y);\n\
                                   \  Point.fromJson(Map json)\n\
                                   \  {\n\
                                   \    x = json[\"x\"];\n\
                                   \    y = json[\"y\"];\n\
                                   \  }\n\
                                   \  num distanceTo(Point other)\n\
                                   \  {\n\
                                   \    var dx = x - other.x;\n\
                                   \    var dy = y - other.y;\n\
                                   \    return sqrt(dx * dx + dy * dy);\n\
                                   \  }\n\
                                   \}"

    it "prints the initializer lists of a constructor" $ do
      let varDeclList ty name = VariableDeclarationList Nothing
                                                        []
                                                        (FCVTType (tyName ty))
                                                        [VariableDeclaration (SimpleIdentifier name) Nothing]
          returnType = SimpleIdentifier' (SimpleIdentifier "Point")
          formals = FormalParameterList [ NormalFormalParameter' (FieldFormalParameter Nothing [] Nothing True (SimpleIdentifier "x"))
                                        , NormalFormalParameter' (FieldFormalParameter Nothing [] Nothing True (SimpleIdentifier "y"))
                                        ]
          exp = InvocationExpression (
                 MethodInvocation Nothing
                                  (SimpleIdentifier "sqrt")
                                  Nothing
                                  (ArgumentList [BinaryExpression (BinaryExpression (identExp "x") "*" (identExp "x"))
                                                                  "+"
                                                                  (BinaryExpression (identExp "y") "*" (identExp "y"))]))
          classMembers = [ FieldDeclaration Nothing [] False (varDeclList "num" "x")
                         , FieldDeclaration Nothing [] False (varDeclList "num" "y")
                         , FieldDeclaration Nothing [] False (varDeclList "num" "distanceFromOrigin")
                         , ConstructorDeclaration Nothing
                                                  []
                                                  False
                                                  False
                                                  False
                                                  returnType
                                                  Nothing
                                                  formals
                                                  [ ConstructorFieldInitializer False (SimpleIdentifier "x") (identExp "x")
                                                  , ConstructorFieldInitializer False (SimpleIdentifier "y") (identExp "y")
                                                  , ConstructorFieldInitializer False (SimpleIdentifier "distanceFromOrigin") exp
                                                  ]
                                                  Nothing
                                                  Nothing
                         ]
          clazz = ClassDeclaration Nothing
                                   []
                                   False
                                   (SimpleIdentifier "Point")
                                   Nothing
                                   Nothing
                                   Nothing
                                   Nothing
                                   classMembers

      prettyPrint clazz `shouldBe` "class Point\n\
                                   \{\n\
                                   \  num x;\n\
                                   \  num y;\n\
                                   \  num distanceFromOrigin;\n\
                                   \  Point(this.x, this.y) : x = x,\n\
                                   \                          y = y,\n\
                                   \                          distanceFromOrigin = sqrt(x * x + y * y);\n\
                                   \}"

    it "prints the implement clause" $ do
      let implements = ImplementsClause [tyName "Comparable", tyName "Location"]
          clazz = ClassDeclaration Nothing
                                   []
                                   False
                                   (SimpleIdentifier "Point")
                                   Nothing
                                   Nothing
                                   Nothing
                                   (Just implements)
                                   []
      prettyPrint clazz `shouldBe` "class Point implements Comparable, Location\n\
                                  \{\n\
                                  \}"

    it "prints the metadata" $ do
      let annotation = Annotation (SimpleIdentifier' (SimpleIdentifier "override")) Nothing Nothing
          methodFormals = FormalParameterList [ NormalFormalParameter' (
                                                  FieldFormalParameter Nothing
                                                                       []
                                                                       (Just (FCVTType (tyName "Invocation")))
                                                                       False
                                                                       (SimpleIdentifier "mirror"))
                                              ]
          methodDecl = MethodDeclaration Nothing
                                         [annotation]
                                         False
                                         Nothing
                                         (Just (tyName "void"))
                                         Empty
                                         False
                                         (SimpleIdentifier "noSuchMethod")
                                         Nothing
                                         (Just methodFormals)
                                         (BlockFunctionBody Sync (Block []))
      prettyPrint methodDecl `shouldBe` "@override\n\
                                        \void noSuchMethod(Invocation mirror)\n\
                                        \{\n\
                                        \}"

main :: IO ()
main = hspec $ do
  prettyPrintSpec