{- Backend base function. Defines the type of the backend and some usefull - functions -} module BNFC.Backend.Base ( Backend , MkFiles , execBackend , mkfile , liftIO , writeFiles ) where import BNFC.Utils (writeFileRep) import Control.Monad.Writer import System.Directory (createDirectoryIfMissing) import System.FilePath (dropFileName, ()) -- | Define the type of the backend functions For more purity, instead of -- having each backend writing the generated files to disk, they return a list -- of pairs containing the (relative) file path and the file content. This -- allow for 1) easier testing, 2) implement common options like changing the -- output dir or providing a diff instead of overwritting the files on a -- highter level and 3) more purity. -- -- The writer monad provide a more conveignent api to generate the list. Note -- that we still use the IO monad for now because some backend insist on -- printing stuff to the screen while generating the files. type MkFiles a = WriterT [(FilePath, String)] IO a type Backend = MkFiles () -- | Named after execWriter, this function execute the given backend -- and returns the generated file paths and contents. execBackend :: MkFiles () -> IO [(FilePath, String)] execBackend = execWriterT -- | A specialized version of tell that adds a file and its content to the -- list of generated files mkfile :: FilePath -> String -> MkFiles () mkfile path content = tell [(path,content)] -- | Write a set of files to disk. the first argument is the root directory -- inside which all the generated files will be written. This root directory -- and sub-directories will be created as needed (ex: if the files contains a -- a/b/file.txt, `writeFiles` will create the directories `$ROOT/a` and -- `$ROOT/a/b`) writeFiles :: FilePath -> MkFiles () -> IO () writeFiles root fw = do -- First we check that the directory exists fb <- execBackend fw createDirectoryIfMissing True root mapM_ (uncurry writeFile') fb where writeFile' :: FilePath -> String -> IO () writeFile' path content = createDirectoryIfMissing True (root dropFileName path) >> writeFileRep (root path) content