content: {-# LANGUAGE OverloadedStrings #-}
  
  module Language.Fortran.PrettyPrintSpec where
  
  import Prelude hiding (mod)
  
  import Data.Data
  import Data.Generics.Uniplate.Operations
  import Data.Maybe (catMaybes)
  
  import Language.Fortran.AST as LFA
  import Language.Fortran.AST.Literal.Boz
  import Language.Fortran.Version
  import Language.Fortran.PrettyPrint
  
  import Text.PrettyPrint hiding ((<>))
  import Text.PrettyPrint.GenericPretty
  
  import Test.Hspec
  import TestUtil
  
  checkAll :: forall a b c . (Out c, Data c, Data a, Data b)
           => (b -> Maybe c) -> (c -> Spec) -> a -> Spec
  checkAll restrict check t =
      describe ("Testing on " ++ show (length inputs) ++ " nodes")
        $ mapM_ check inputs
    where
      inputs = catMaybes [ restrict b | b <- universeBi t :: [b] ]
  
  spec :: Spec
  spec =
    describe "Pretty printer tests" $ do
      describe "Dimension declarator" $ do
        it "Prints left bound dimension declarator" $ do
          let dd = DimensionDeclarator () u (Just $ intGen 42) Nothing
          pprint Fortran90 dd Nothing `shouldBe` "42:"
  
        it "Prints right bound dimension declarator" $ do
          let dd = DimensionDeclarator () u Nothing (Just $ intGen 42)
          pprint Fortran90 dd Nothing `shouldBe` "42"
  
        it "Prints bounded dimension declarator" $ do
          let dd = DimensionDeclarator () u (Just $ intGen 24) (Just $ intGen 42)
          pprint Fortran90 dd Nothing `shouldBe` "24:42"
  
      describe "Selector" $ do
        it "prints Fortran 77 selector" $ do
          let sel = Selector () u (Just $ intGen 42) Nothing
          pprint Fortran77 sel Nothing `shouldBe` "*42"
          let ksel = Selector () u Nothing (Just $ intGen 4)
          pprint Fortran77 ksel Nothing `shouldBe` "*4"
          let vsel = Selector () u (Just $ varGen "v") Nothing
          pprint Fortran77 vsel Nothing `shouldBe` "*(v)"
  
        it "prints Fortran 90 selector" $ do
          let sel = Selector () u (Just $ intGen 42) (Just $ intGen 24)
          pprint Fortran90 sel Nothing `shouldBe` "(len=42, kind=24)"
  
      describe "Use" $
        it "prints renaming" $ do
          let renaming = UseRename () u (varGen "x") (varGen "y")
          pprint Fortran90 renaming Nothing `shouldBe` "x => y"
  
      describe "Control pair" $
        it "prints named control pair" $ do
          let cp = ControlPair () u (Just "errno") (intGen 42)
          pprint Fortran77Extended cp Nothing `shouldBe` "errno=42"
  
      describe "Implicit list" $
        it "prints mixed implicit lists" $ do
          let typ = TypeSpec () u TypeInteger Nothing
          let impEls = [ ImpElement () u 'x' Nothing
                       , ImpElement () u 'a' (Just 'z')
                       , ImpElement () u 'o' Nothing ]
          let impList = ImpList () u typ (AList () u impEls)
          pprint Fortran90 impList Nothing `shouldBe` "integer (x, a-z, o)"
  
      describe "Common group" $ do
        let globs = [ declVarGen "x", declVarGen "y", declVarGen "z" ]
  
        it "prints anonymous common group" $ do
          let group = CommonGroup () u Nothing (AList () u globs)
          pprint Fortran66 group Nothing `shouldBe` "// x, y, z"
  
        it "prints named common group" $ do
          let group = CommonGroup () u (Just $ varGen "my_g") (AList () u globs)
          pprint Fortran66 group Nothing `shouldBe` "/my_g/ x, y, z"
  
      describe "Format item" $
        it "prints hollerith constant" $ do
          let ed = FIHollerith () u (ValHollerith "hello darling")
          pprint Fortran77 ed Nothing `shouldBe` "13hhello darling"
  
      describe "Flush statement" $
        it "prints flush statement" $ do
          let f = StFlush () u (AList () u [ FSUnit () u (intGen 1), FSIOStat () u (varGen "x")
                                           , FSIOMsg () u (varGen "y"), FSErr () u (varGen "z") ])
          pprint Fortran2003 f Nothing `shouldBe` "flush (unit=1, iostat=x, iomsg=y, err=z)"
  
      describe "Value" $ do
        it "prints logical literal with no kind parameter" $ do
          let lit = ValLogical True Nothing
          pprint Fortran77 lit Nothing `shouldBe` ".true."
  
        it "prints logical literal with kind parameter (>=F90)" $ do
          let lit = ValLogical False (Just kp)
              kp  = KindParamInt () u "8"
          pprint Fortran90 lit Nothing `shouldBe` ".false._8"
  
        it "prints BOZ constant with prefix" $ do
          let lit = ValBoz $ Boz (BozPrefixZ Conforming) "123abc" Conforming
          pprint Fortran90 lit Nothing `shouldBe` "z'123abc'"
  
        -- Note that we can't test printing nonconforming BOZs, because the
        -- 'Boz.prettyBoz' that gets used in the 'Pretty' instance refuses to
        -- pretty print nonconforming syntax.
        {-
        it "prints BOZ constant with nonconforming postfix" $ do
          let lit = ValBoz $ Boz (BozPrefixZ Nonconforming) "123abc" Nonconforming
          pprint Fortran90 lit Nothing `shouldBe` "'123abc'x"
        -}
  
      describe "Statement" $ do
        describe "Declaration" $ do
          it "prints 90 style with attributes" $ do
            let sel = Selector () u (Just $ intGen 3) Nothing
            let typeSpec = TypeSpec () u TypeCharacter (Just sel)
            let attrs = [ AttrIntent () u In , AttrPointer () u ]
            let declList =
                  [ Declarator () u (varGen "x") ScalarDecl Nothing (Just $ intGen 42)
                  , Declarator () u (varGen "y") ScalarDecl (Just $ intGen 3) Nothing ]
            let st = StDeclaration () u typeSpec
                                        (Just $ AList () u attrs)
                                        (AList () u declList)
            let expect = "character(len=3), intent(in), pointer :: x = 42, y*3"
            pprint Fortran90 st Nothing `shouldBe` expect
  
          it "prints 77 style" $ do
            let typeSpec = TypeSpec () u TypeInteger Nothing
            let dds = [ DimensionDeclarator () u Nothing (Just $ intGen 5) ]
            let declList =
                  [ Declarator () u (varGen "x") (ArrayDecl (AList () u dds)) Nothing
                              (Just . initGen $ map intGen [1..5])
                  ]
            let st = StDeclaration () u typeSpec Nothing (AList () u declList)
            pprint Fortran77 st Nothing `shouldBe` "integer x(5)/1, 2, 3, 4, 5/"
  
          it "prints ValStar with parens" $ do
            let sel = Selector () u (Just $ ExpValue () u ValStar) Nothing
                typeSpec = TypeSpec () u TypeCharacter (Just sel)
                decl = Declarator () u (varGen "x") ScalarDecl (Just $ ExpValue () u ValStar) Nothing
                st = StDeclaration () u typeSpec Nothing (AList () u [decl])
                expect = "character*(*) x*(*)"
            pprint Fortran77 st Nothing `shouldBe` expect
  
        describe "Intent" $
          it "prints intent statement" $ do
            let exps = [ varGen "x", varGen "y" ]
            let st = StIntent () u In (AList () u exps)
            pprint Fortran90 st Nothing `shouldBe` "intent (in) :: x, y"
  
        describe "Save" $ do
          it "prints lone save statement" $ do
            let st = StSave () u Nothing
            pprint Fortran90 st Nothing `shouldBe` "save"
  
          let st = StSave () u (Just $ AList () u [ varGen "x", varGen "y" ])
  
          it "prints 90 style save statement with vars" $
            pprint Fortran90 st Nothing `shouldBe` "save :: x, y"
  
          it "prints 77 style save statement with vars" $
            pprint Fortran77Extended st Nothing `shouldBe` "save x, y"
  
        describe "Data" $ do
          let groups =
                [ DataGroup () u (AList () u [ varGen "x"])
                                 (AList () u [ intGen 42 ])
                , DataGroup () u (AList () u [ varGen "y"])
                                 (AList () u [ intGen 24 ]) ]
          let st = StData () u (AList () u groups)
  
          it "prints 90 style data statement with multiple groups" $
            pprint Fortran90 st Nothing `shouldBe` "data x/42/, y/24/"
  
          it "prints 77 style data statement with multiple groups" $
            pprint Fortran77Extended st Nothing `shouldBe` "data x/42/ y/24/"
  
        describe "Parameter" $
          it "prints vanilla statement" $ do
            let decls = [ Declarator () u (varGen "x") ScalarDecl Nothing (Just $ intGen 42)
                        , Declarator () u (varGen "y") ScalarDecl Nothing (Just $ intGen 24)
                        ]
            let st = StParameter () u (AList () u decls)
            pprint Fortran90 st Nothing `shouldBe` "parameter (x = 42, y = 24)"
  
        describe "Equivalence" $
          it "prints multiple equivalence groups" $ do
            let equivGroups = [ AList () u [ varGen "x", varGen "y" ]
                              , AList () u [ varGen "z" ] ]
            let st = StEquivalence () u (AList () u equivGroups)
            pprint Fortran90 st Nothing `shouldBe` "equivalence (x, y), (z)"
  
        describe "Equivalence" $
          it "prints entry point with arguments and result specified" $ do
            let aargs = AList () u [ varGen "x", varGen "y" ]
            let result = varGen "z"
            let entry = StEntry () u (varGen "func") (Just aargs) (Just result)
            pprint Fortran90 entry Nothing `shouldBe` "entry func (x, y) result (z)"
  
        describe "Do" $ do
          it "prints infinity do" $ do
            let stDo = StDo () u Nothing Nothing Nothing
            pprint Fortran90 stDo Nothing `shouldBe` "do"
  
          let doInit = StExpressionAssign () u (varGen "i") (intGen (-1))
          let doSpec = DoSpecification () u doInit (intGen 5) Nothing
  
          it "prints labeled do" $ do
            let stDo = StDo () u Nothing (Just $ intGen 42) (Just doSpec)
            pprint Fortran90 stDo Nothing `shouldBe` "do 42 i = -1, 5"
  
          it "prints named do" $ do
            let stDo = StDo () u (Just "mistral") Nothing (Just doSpec)
            pprint Fortran90 stDo Nothing `shouldBe` "mistral: do i = -1, 5"
  
        describe "If" $ do
          it "prints arithmetic if" $ do
            let arIf = StIfArithmetic () u (intGen 0)
                  (intGen 10) (intGen 20) (intGen 30)
            pprint Fortran66 arIf Nothing `shouldBe` "if (0) 10, 20, 30"
  
          it "prints logical if" $ do
            let as = StExpressionAssign () u (varGen "x") (intGen 42)
            let logIf = StIfLogical () u valFalse as
            pprint Fortran90 logIf Nothing `shouldBe` "if (.false.) x = 42"
  
        describe "Case" $ do
          it "prints select case" $ do
            let sc = StSelectCase () u Nothing (varGen "x")
            pprint Fortran90 sc Nothing `shouldBe` "select case (x)"
  
          it "prints filled case" $ do
            let caseRanges =
                  [ IxRange () u (Just $ intGen 0) (Just $ intGen 100) Nothing
                  , IxSingle () u Nothing (intGen 10) ]
            let casee = StCase () u Nothing (Just $ AList () u caseRanges)
            pprint Fortran90 casee Nothing `shouldBe` "case (0:100, 10)"
  
          it "prints named default case" $ do
            let casee = StCase () u (Just "mistral") Nothing
            pprint Fortran90 casee Nothing `shouldBe` "case default mistral"
  
        describe "Function statement" $
          it "prints function statement" $ do
            let args = [ varGen "x", varGen "y" ]
            let fSt = StFunction () u (varGen "pi") (AList () u args) (varGen "x")
            pprint Fortran90 fSt Nothing `shouldBe` "pi(x, y) = x"
  
        describe "Stop" $
          it "prints stop with code" $
            pprint Fortran66 (StStop () u (Just $ intGen 1)) Nothing `shouldBe` "stop 1"
  
        describe "IO" $
          describe "Print" $
            it "prints vanilla print statement" $ do
              let st = StPrint () u starVal (Just $ AList () u [ intGen 42 ])
              pprint Fortran90 st Nothing `shouldBe` "print *, 42"
  
        describe "Allocation" $
          describe "Allocate" $ do
            it "prints allocate statement" $ do
              let stat = AOStat () u (varGen "s")
              let st = StAllocate () u Nothing (AList () u [ varGen "x" ]) (Just (AList () u [stat]))
              pprint Fortran90 st Nothing `shouldBe` "allocate (x, stat=s)"
            it "prints allocate statement with type spec" $ do
              let stat = AOStat () u (varGen "s")
              let sel = Selector () u (Just (intGen 30)) Nothing
              let ty = TypeSpec () u TypeCharacter (Just sel)
              let st = StAllocate () u (Just ty) (AList () u [ varGen "x" ]) (Just (AList () u [stat]))
              pprint Fortran2003 st Nothing `shouldBe` "allocate (character(len=30) :: x, stat=s)"
  
        describe "Where" $
          it "prints statement" $ do
            let stAssign = StExpressionAssign () u (varGen "x") (intGen 42)
            let stWhere = StWhere () u valTrue stAssign
            pprint Fortran90 stWhere Nothing `shouldBe` "where (.true.) x = 42"
  
        describe "Use" $ do
          it "prints exclusive use statement" $ do
            let aRenames = AList () u [ UseRename () u (varGen "x") (varGen "y") ]
            let st = StUse () u (varGen "my_mod") Nothing Exclusive (Just aRenames)
            pprint Fortran90 st Nothing `shouldBe` "use my_mod, only: x => y"
  
          it "prints intrinsic use statement" $ do
            let aRenames = AList () u [ UseRename () u (varGen "x") (varGen "y") ]
            let st = StUse () u (varGen "my_mod") (Just ModIntrinsic) Exclusive (Just aRenames)
            pprint Fortran2003 st Nothing `shouldBe` "use, intrinsic :: my_mod, only: x => y"
  
          it "prints non_intrinsic use statement" $ do
            let aRenames = AList () u [ UseRename () u (varGen "x") (varGen "y") ]
            let st = StUse () u (varGen "my_mod") (Just ModNonIntrinsic) Exclusive (Just aRenames)
            pprint Fortran2003 st Nothing `shouldBe` "use, non_intrinsic :: my_mod, only: x => y"
  
      let decrementRHS = ExpBinary () u Subtraction (varGen "i") (intGen 1)
      let st1 = StPrint () u starVal (Just $ AList () u [ varGen "i" ])
      let st2 = StExpressionAssign () u (varGen "i") decrementRHS
      let body = [ BlStatement () u Nothing st1 , BlStatement () u Nothing st2 ]
  
      describe "Blocks" $ do
        describe "Comment" $ do
          let blComment = BlComment () u (Comment " si vis pacem para bellum")
  
          it "prints 90 style comment" $
            pprint Fortran90 blComment Nothing `shouldBe` "! si vis pacem para bellum\n"
  
          it "prints 66 style comment" $
            pprint Fortran66 blComment Nothing `shouldBe` "c si vis pacem para bellum\n"
  
        describe "Statement" $
          it "prints vanilla print" $ do
            let st = StPrint () u starVal Nothing
            let bl = BlStatement () u (Just $ intGen 42) st
            pprint Fortran90 bl Nothing `shouldBe` "42 print *\n"
  
        describe "Interface" $
          it "prints interface block" pending
  
        describe "Do While" $ do
          it "prints simple do while loop" $ do
            let cond = ExpBinary () u LFA.GT (varGen "i") (intGen 42)
            let bl = BlDoWhile () u Nothing (Just "my_block") Nothing cond body Nothing
            let expect = unlines [ "my_block: do while ((i > 42))"
                                 , "print *, i"
                                 , "i = (i - 1)"
                                 , "end do my_block" ]
            pprint Fortran90 bl Nothing `shouldBe` text expect
  
          it "prints a labelled do while loop" $ do
            let cond = ExpBinary () u LFA.GT (varGen "i") (intGen 42)
            let bl = BlDoWhile () u Nothing Nothing (Just (intGen 10)) cond body Nothing
            let expect = unlines [ "do 10 while ((i > 42))"
                                 , "print *, i"
                                 , "i = (i - 1)" ]
            pprint Fortran90 bl Nothing `shouldBe` text expect
  
        describe "Do" $ do
          let iAssign = StExpressionAssign () u (varGen "i") (intGen 1)
          let doSpec = DoSpecification () u iAssign (intGen 9) (Just (intGen 2))
  
          it "prints 90 style do loop" $ do
            let bl = BlDo () u Nothing Nothing Nothing (Just doSpec) body Nothing
            let expect = unlines [ "do i = 1, 9, 2"
                                 , "print *, i"
                                 , "i = (i - 1)"
                                 , "end do" ]
            pprint Fortran90 bl Nothing `shouldBe` text expect
  
          it "prints named infinite do loop" $ do
            let bl = BlDo () u Nothing (Just "joker") Nothing Nothing body Nothing
            let expect = unlines [ "joker: do"
                                 , "print *, i"
                                 , "i = (i - 1)"
                                 , "end do joker" ]
            pprint Fortran90 bl Nothing `shouldBe` text expect
  
          it "prints named labeled do loop" $ do
            let bl = BlDo () u Nothing (Just "joker") (Just $ intGen 42) (Just doSpec) body (Just $ intGen 42)
            let expect = unlines [ "joker: do 42 i = 1, 9, 2"
                                 , "print *, i"
                                 , "i = (i - 1)"
                                 , "42 end do joker" ]
            pprint Fortran90 bl Nothing `shouldBe` text expect
  
          it "prints vanilla labeled do loop" $ do
            let body2 = body ++ [ BlStatement () u (Just $ intGen 42) (StContinue () u) ]
            let bl = BlDo () u Nothing Nothing (Just $ intGen 42) (Just doSpec) body2 (Just $ intGen 42)
            let expect = unlines [ "      do 42 i = 1, 9, 2"
                                 , "        print *, i"
                                 , "        i = (i - 1)"
                                 , "42      continue" ]
            pprint Fortran77 bl (Just 6) `shouldBe` text expect
  
        describe "If" $ do
          it "prints vanilla structured if" $ do
            let bl = BlIf () u Nothing Nothing clauses Nothing Nothing
                clauses = (valTrue, body) :| []
                expect = unlines [ "if (.true.) then"
                                 , "print *, i"
                                 , "i = (i - 1)"
                                 , "end if" ]
            pprint Fortran90 bl Nothing `shouldBe` text expect
  
          it "prints multiple condition named structured if" $ do
            let clauses = (valTrue, body) :| [ (valFalse, body), (valTrue, body) ]
                elseBlock = Just body
                bl = BlIf () u Nothing (Just "mistral") clauses elseBlock Nothing
                expect = unlines [ "mistral: if (.true.) then"
                                 , "  print *, i"
                                 , "  i = (i - 1)"
                                 , "else if (.false.) then"
                                 , "  print *, i"
                                 , "  i = (i - 1)"
                                 , "else if (.true.) then"
                                 , "  print *, i"
                                 , "  i = (i - 1)"
                                 , "else"
                                 , "  print *, i"
                                 , "  i = (i - 1)"
                                 , "end if mistral" ]
            pprint Fortran90 bl (Just 0) `shouldBe` text expect
  
        describe "Case" $
          it "prints multi-case select case construct" $ do
            let range = IxRange () u (Just $ intGen 2) (Just $ intGen 4) Nothing
                clauses = [ ( AList () u [range] , body )
                          , ( AList () u [ IxSingle () u Nothing (intGen 7) ] , body )
                          ]
                caseDef = Just body
                bl = BlCase () u Nothing Nothing (varGen "x") clauses caseDef (Just (intGen 42))
                expect = unlines [ "select case (x)"
                                 , "  case (2:4)"
                                 , "    print *, i"
                                 , "    i = (i - 1)"
                                 , "  case (7)"
                                 , "    print *, i"
                                 , "    i = (i - 1)"
                                 , "  case default"
                                 , "    print *, i"
                                 , "    i = (i - 1)"
                                 , "42 end select" ]
            pprint Fortran90 bl (Just 0) `shouldBe` text expect
  
        describe "Associate" $
          it "prints multi-abbreviation associate block (Fortran2003)" $ do
            let bl      = BlAssociate () u Nothing Nothing abbrevs body' Nothing
                body'   = [blStmtPrint "x", blStmtPrint "y"]
                blStmtPrint x = BlStatement () u Nothing (stmtPrint x)
                stmtPrint x = StPrint () u starVal (Just $ AList () u [ varGen x ])
                abbrevs = AList () u [abbrev "x" (expValVar "a"), abbrev "y" (expBinVars Multiplication "a" "b")]
                abbrev var expr = ATuple () u (expValVar var) expr
                expValVar x = ExpValue () u (ValVariable x)
                expBinVars op x1 x2 = ExpBinary () u op (expValVar x1) (expValVar x2)
            let expect = unlines [ "associate (x => a, y => (a * b))"
                                 , "  print *, x"
                                 , "  print *, y"
                                 , "end associate" ]
            pprint Fortran2003 bl (Just 0) `shouldBe` text expect
  
      describe "Program units" $ do
        describe "Main" $ do
          it "prints 90 style main without sub programs" $ do
            let main = PUMain () u (Just "main") body Nothing
            let expect = unlines [ "program main"
                                 , "print *, i"
                                 , "i = (i - 1)"
                                 , "end program main" ]
            pprint Fortran90 main Nothing `shouldBe` text expect
  
          it "prints 66 style main" $ do
            let main = PUMain () u Nothing body Nothing
            let expect = unlines [ "      print *, i"
                                 , "      i = (i - 1)"
                                 , "      end" ]
            pprint Fortran66 main (Just 0) `shouldBe` text expect
  
        describe "Module" $ do
          it "prints module without sub programs" $ do
            let mod = PUModule () u "my_mod" body Nothing
            let expect = unlines [ "module my_mod"
                                 , "  print *, i"
                                 , "  i = (i - 1)"
                                 , "end module my_mod" ]
            pprint Fortran90 mod (Just 0) `shouldBe` text expect
  
          it "prints module with sub programs" $ do
            let sub = PUSubroutine () u emptyPrefixSuffix "sub" Nothing body Nothing
            let mod = PUModule () u "my_mod" body (Just [ sub ])
            let expect = unlines [ "   module my_mod"
                                 , "     print *, i"
                                 , "     i = (i - 1)"
                                 , ""
                                 , "     contains"
                                 , ""
                                 , "     subroutine sub"
                                 , "       print *, i"
                                 , "       i = (i - 1)"
                                 , "     end subroutine sub"
                                 , "   end module my_mod" ]
            pprint Fortran90 mod (Just 3) `shouldBe` text expect
  
        describe "Subroutine" $ do
          it "prints recursive subroutine with args without sub programs" $ do
            let args = AList () u [ varGen "x", varGen "y", varGen "z" ]
            let sub = PUSubroutine () u (Just (AList () u [PfxRecursive () u]), emptySuffixes) "sub" (Just args) body Nothing
            let expect = unlines [ "recursive subroutine sub(x, y, z)"
                                 , "print *, i"
                                 , "i = (i - 1)"
                                 , "end subroutine sub" ]
            pprint Fortran90 sub Nothing `shouldBe` text expect
  
          it "prints 66 style subroutine without args" $ do
            let mod = PUSubroutine () u emptyPrefixSuffix "sub" Nothing body Nothing
            let expect = unlines [ "      subroutine sub"
                                 , "        print *, i"
                                 , "        i = (i - 1)"
                                 , "      end" ]
            pprint Fortran66 mod Nothing `shouldBe` text expect
  
        describe "Function" $ do
          let tSpec = Just $ TypeSpec () u TypeInteger Nothing
  
          it "prints function with args with result without sub programs" $ do
            let args = AList () u [ varGen "x", varGen "y", varGen "z" ]
            let res = Just $ varGen "i"
            let fun = PUFunction () u tSpec emptyPrefixSuffix "f" (Just args) res body Nothing
            let expect = unlines [ "  integer function f(x, y, z) result(i)"
                                 , "    print *, i"
                                 , "    i = (i - 1)"
                                 , "  end function f" ]
            pprint Fortran90 fun (Just 2) `shouldBe` text expect
  
      describe "Program file" $
        it "prints simple program file" $ do
          let body' = [ BlStatement () u Nothing (StContinue () u) ]
          let pu = PUModule () u "my_mod" body' Nothing
          let com = PUComment () u (Comment "hello!")
          let pf = ProgramFile mi77 [com, pu, com, pu, com, com]
          let expect = unlines [ "!hello!"
                               , "module my_mod"
                               , "  continue"
                               , "end module my_mod"
                               , "!hello!"
                               , "module my_mod"
                               , "  continue"
                               , "end module my_mod"
                               , "!hello!"
                               , "!hello!" ]
          pprint Fortran90 pf (Just 0) `shouldBe` text expect
  
      describe "Continuation reformatting" $ do
        it "continuates a too-long 77 style statement" $ do
          let input  = "      integer, parameter :: "
                    <> "very_awfully_really_quite_pointlessly_long_s"
                    <> "illy_variable_name = 1"
              expect = "      integer, parameter :: "
                    <> "very_awfully_really_quite_pointlessly_long_s"
                    <> "&\n     &"
                    <> "illy_variable_name = 1"
          reformatMixedFormInsertContinuations input `shouldBe` expect
        it "correctly handles 72 character long statements" $ do
          let input  = "      integer*4 :: x, y, z\n"
                    <> "        x = +(((y - (z - 48)) * ((z + 62) - z)) + (((- z) * x) / (- x)))"
              expect = "      integer*4 :: x, y, z\n"
                    <> "        x = +(((y - (z - 48)) * ((z + 62) - z)) + (((- z) * x) / (- x)))"
          reformatMixedFormInsertContinuations input `shouldBe` expect
        it "correctly handles 73 character long statements" $ do
          let input  = "      integer*4 :: x, y, z\n"
                    <> "        x = + (((y - (z - 48)) * ((z + 62) - z)) + (((- z) * x) / (- x)))"
              expect = "      integer*4 :: x, y, z\n"
                    <> "        x = + (((y - (z - 48)) * ((z + 62) - z)) + (((- z) * x) / (- x))&\n"
                    <> "     &)"
          reformatMixedFormInsertContinuations input `shouldBe` expect
  
        it "does not continuate a long mixed-form comment line" $ do
          let input  = "      ! a very long, long comment that ends up"
                    <> " exceeding both the F77 and F90 maximum line lengths"
                    <> " (~72 and ~132 respectively), but should not be"
                    <> " continuated (since they're ignored by compilers anyway)"
          reformatMixedFormInsertContinuations input `shouldBe` input
  
        it "does not continuate a long F77 comment line" $ do
          let input  = "c F77 comments begin with a 'c' in the first column"
                    <> " position, so they're easy to handle"
          reformatMixedFormInsertContinuations input `shouldBe` input
  
        it "adds continuations idempotently" $ do
          let input  = "      integer, parameter :: "
                    <> "very_awfully_really_quite_pointlessly_long_s"
                    <> "illy_variable_name = 1"
              pass1 = reformatMixedFormInsertContinuations input
              pass2 = reformatMixedFormInsertContinuations pass1
          pass2 `shouldBe` pass1
  
        it "does not continuate lines exactly 72 columns" $ do
          let input  = "      integer, parameter :: "
                    <> "variable_id_making_line_exactly_72_chars = 1"
          reformatMixedFormInsertContinuations input `shouldBe` input
  
  valueExpressions :: Expression () -> Maybe (Expression ())
  valueExpressions e@ExpValue{} = Just e
  valueExpressions _ = Nothing
  