{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE TypeApplications #-}
module FreeC.Frontend
( Frontend(..)
, FrontendParser
, FrontendSimplifier
, frontends
, showFrontends
, defaultFrontend
) where
import Control.Monad.Extra ( ifM )
import Control.Monad.IO.Class
import Data.List ( intercalate )
import qualified Data.Map.Strict as Map
import qualified Language.Haskell.Exts.Syntax as HSE
import System.Directory
( createDirectoryIfMissing )
import System.FilePath
import FreeC.Application.Options
import FreeC.Frontend.Haskell.Parser
( parseHaskellModuleWithComments )
import FreeC.Frontend.Haskell.PatternMatching
( transformPatternMatching )
import FreeC.Frontend.Haskell.Pretty ()
import FreeC.Frontend.Haskell.Simplifier
import FreeC.Frontend.IR.Parser
import FreeC.IR.SrcSpan
import qualified FreeC.IR.Syntax as IR
import FreeC.Monad.Application
import FreeC.Pretty
( showPretty, writePrettyFile )
type FrontendParser decls = SrcFile -> Application (IR.ModuleOf decls)
type FrontendSimplifier decls = IR.ModuleOf decls -> Application IR.Module
data Frontend = forall decls. Frontend
{ frontendName :: String
, frontendParseFile :: FrontendParser decls
, frontendSimplifyModule :: FrontendSimplifier decls
}
frontends :: Map.Map String Frontend
frontends
= Map.fromList [(frontendName f, f) | f <- [haskellFrontend, irFrontend]]
showFrontends :: String
showFrontends = '`' : intercalate "`, `" (Map.keys frontends) ++ "`"
defaultFrontend :: String
defaultFrontend = frontendName haskellFrontend
irFrontend :: Frontend
irFrontend = Frontend { frontendName = "ir"
, frontendParseFile = parseIR @IR.Module
, frontendSimplifyModule = return
}
parseHaskell :: FrontendParser (HSE.Module SrcSpan)
parseHaskell inputFile = do
(inputModule, comments) <- parseHaskellModuleWithComments inputFile
liftConverter $ simplifyModuleHeadWithComments inputModule comments
simplifyHaskell :: FrontendSimplifier (HSE.Module SrcSpan)
simplifyHaskell inputModule = do
inputModule' <- transformInputModule inputModule
liftConverter $ simplifyModuleBody inputModule'
haskellFrontend :: Frontend
haskellFrontend = Frontend { frontendName = "haskell"
, frontendParseFile = parseHaskell
, frontendSimplifyModule = simplifyHaskell
}
transformInputModule :: IR.ModuleOf (HSE.Module SrcSpan)
-> Application (IR.ModuleOf (HSE.Module SrcSpan))
transformInputModule inputModule = ifM (inOpts optTransformPatternMatching)
transformPatternMatching' (return inputModule)
where
transformPatternMatching' :: Application (IR.ModuleOf (HSE.Module SrcSpan))
transformPatternMatching' = do
outputModule <- liftConverter
$ transformPatternMatching (IR.modContents inputModule)
maybeDumpDir <- inOpts optDumpTransformedModulesDir
case maybeDumpDir of
Nothing -> return inputModule { IR.modContents = outputModule }
Just dumpDir -> do
let modName = IR.modName inputModule
modPath = map (\c -> if c == '.' then '/' else c) modName
dumpFile = dumpDir </> modPath <.> "hs"
dumpContents = showPretty outputModule
liftIO $ createDirectoryIfMissing True (takeDirectory dumpFile)
liftIO $ writePrettyFile dumpFile dumpContents
dumpModule <- parseHaskell (mkSrcFile dumpFile dumpContents)
return dumpModule { IR.modPragmas = IR.modPragmas inputModule }