packages feed

language-Modula2-0.1: test/Test.hs

{-# Language FlexibleInstances #-}
module Main where

import Control.Monad (unless)
import Data.Either.Validation (Validation(..))
import Data.Functor.Identity (Identity(Identity))
import Data.List (isSuffixOf)
import Data.List.NonEmpty (NonEmpty((:|)))
import Data.Text (Text, unpack)
import Data.Text.IO (readFile)
import Data.Text.Prettyprint.Doc (Pretty(pretty), layoutPretty, defaultLayoutOptions)
import Data.Text.Prettyprint.Doc.Render.Text (renderStrict)
import System.Directory (doesDirectoryExist, listDirectory)
import System.FilePath.Posix (combine)
import Test.Tasty (TestTree, defaultMain, testGroup)
import Test.Tasty.HUnit (assertFailure, assertEqual, testCase)
import Text.Grampa (failureDescription)

import qualified Transformation.Rank2 as Rank2

import qualified Language.Oberon.Reserializer as Reserializer

import Language.Modula2 (parseModule, parseAndSimplifyModule, Placed, Version(..))
import Language.Modula2.AST (Language, Module)
import qualified Language.Modula2.ISO.AST as ISO (Language)
import qualified Language.Modula2.ConstantFolder -- brings in HasField instances
import qualified Language.Modula2.ISO.ConstantFolder -- brings in HasField instances

import Prelude hiding (readFile)

main = exampleTree "" "examples/" >>= defaultMain . testGroup "Modula-2"

width = 80
contextLines = 3

unparsables = map ("examples/Modula-2_Libraries/" <>)
              ["andrea-m2/applications/eth-hamburg/life/lifemodul.def",
               "andrea-m2/examples/generic/base3-2.mod",
               "andrea-m2/examples/generic/somebits.mod",
               "andrea-m2/lib/generic/bitmapope.mod",
               "andrea-m2/lib/generic/bitsetfun.mod",
               "andrea-m2/lib/generic/largesets.mod",
               "andrea-m2/lib/generic/packarray.mod",
               "andrea-m2/lib/generic/shufflefa.mod",
               "andrea-m2/lib/multiscope/bitmapop.mod",
               "andrea-m2/lib/eth-hamburg/blockio.mod",
               "andrea-m2/lib/eth-hamburg/cmdline.mod",
               "andrea-m2/lib/eth-hamburg/dateopera.mod",
               "andrea-m2/lib/eth-hamburg/directacc.mod",
               "andrea-m2/lib/eth-hamburg/extendstr.mod",
               "andrea-m2/lib/eth-hamburg/foreignco.mod",
               "andrea-m2/lib/eth-hamburg/fortrandi.def",
               "andrea-m2/lib/eth-hamburg/fortranfo.def",
               "andrea-m2/lib/eth-hamburg/fortranjp.def",
               "andrea-m2/lib/eth-hamburg/getcharac.mod",
               "andrea-m2/lib/eth-hamburg/memorysta.mod",
               "andrea-m2/lib/eth-hamburg/moremath.mod",
               "andrea-m2/lib/eth-hamburg/realinout.mod",
               "andrea-m2/lib/eth-hamburg/simplescr.mod",
               "andrea-m2/lib/eth-hamburg/simplewin.mod",
               "andrea-m2/lib/eth-hamburg/systemcon.def",
               "C.-Lins_Modula-2_Software_Component_Library/Vol4/sorting/timesort.mod",
               "PMOS/def/internet.def",
               "PMOS/def/kbdriver.def",
               "PMOS/def/miscpmos.def",
               "PMOS/def/netdb.def",
               "PMOS/sources/notdone/fastwind.mod",
               "PMOS/sources/general/graphics.mod",
               "PMOS/sources/general/lowlevel.def",
               "PMOS/sources/general/mouse33.mod",
               "PMOS/sources/general/play3.mod",
               "PMOS/sources/general/serialmo.mod",
               "PMOS/sources/special/lowlevel.mod"]
              <>
              ["examples/Dipl/BUFFERS.MOD",
               "examples/Dipl/LIBRARY.MOD",
               "examples/Dipl/MACHINE.MOD",
               "examples/Dipl/MEMORY.MOD",
               "examples/Dipl/NAMES.MOD",
               "examples/Dipl/M2/BUFFERS.MOD",
               "examples/Dipl/M2/LIBRARY.MOD",
               "examples/Dipl/M2/MACHINE.MOD",
               "examples/Dipl/M2/MEMORY.MOD",
               "examples/Dipl/M2/NAMES.MOD"]

exampleTree :: FilePath -> FilePath -> IO [TestTree]
exampleTree ancestry path =
   do let fullPath = combine ancestry path
      putStrLn fullPath
      isDir <- doesDirectoryExist fullPath
      if isDir
         then (:[]) . testGroup path . concat <$> (listDirectory fullPath >>= mapM (exampleTree fullPath))
         else return . (:[]) . testCase path . unless (any (`isSuffixOf` path) [".BNF", ".bnf", ".GRM"]) $
              do moduleSource <- readFile fullPath
                 (originalModule, prettyModule) <- prettyFile fullPath moduleSource
                 (originalModule', prettyModule') <- prettyFile fullPath originalModule
                 (originalModule'', prettyModule'') <- prettyFile fullPath prettyModule
                 putStrLn fullPath
                 assertEqual "original=original" originalModule originalModule'
                 assertEqual "pretty=pretty" prettyModule prettyModule'
                 assertEqual "pretty=pretty'" prettyModule prettyModule''
                 assertEqual "original=pretty" originalModule'' prettyModule''

prettyFile :: FilePath -> Text -> IO (Text, Text)
prettyFile path src
   | path `elem` unparsables = case (parsedModule1, parsedModule2) of
       (Left err1, Left err2)
          | err1 == err2 -> putStrLn (unpack $ failureDescription src err1 4) *> pure (src, src)
          | otherwise -> putStrLn (unpack $ failureDescription src err1 4 <> failureDescription src err2 4) *> pure (src, src)
       _ -> error (path ++ " is supposed to be unparseable")
   | otherwise = case parsedModule1 of
       Right [tree] -> return (Reserializer.reserialize tree, renderStrict $ layoutPretty defaultLayoutOptions $ pretty tree)
       Right trees -> error (show (length trees) ++ " ambiguous parses.")
       Left err1 -> case parsedModule2 of
         Left err2
          | err1 == err2 -> error (unpack $ failureDescription src err1 4)
          | otherwise -> error (unpack $ failureDescription src err1 4 <> failureDescription src err2 4)
         Right [tree] -> return (Reserializer.reserialize tree, renderStrict $ layoutPretty defaultLayoutOptions $ pretty tree)
         Right trees -> error (show (length trees) ++ " ambiguous parses.")
   where parsedModule1 = parseAndSimplifyModule Report src
         parsedModule2 = parseAndSimplifyModule ISO src

instance {-# overlaps #-} Pretty a => Pretty (Placed a) where
   pretty = pretty . snd

instance Pretty (Module Language Language Placed Placed) where
   pretty m = pretty ((Identity . snd) Rank2.<$> m)
instance Pretty (Module ISO.Language ISO.Language Placed Placed) where
   pretty m = pretty ((Identity . snd) Rank2.<$> m)