module Language.Java.Paragon.Monad.PiReader ( module Language.Java.Paragon.Monad.Base, PiPath, PiReader, runPiReader, MonadPR(..), getPiPath, doesPkgExist, doesTypeExist, getPkgContents, getTypeContents, getPiPathContents ) where import Language.Java.Paragon.Syntax import Language.Java.Paragon.Pretty import Language.Java.Paragon.Parser import Language.Java.Paragon.Interaction import Language.Java.Paragon.Monad.Base import System.FilePath import System.Directory import Control.Monad import Control.Applicative piReaderModule :: String piReaderModule = libraryBase ++ ".Monad.PiReader" -- import System.FilePath type PiPath = [FilePath] -- Should be a choice of many different newtype PiReader a = PiReader ( PiPath -> BaseM a ) runPiReader :: PiPath -> PiReader a -> BaseM a runPiReader pp (PiReader f) = f pp instance Monad PiReader where return x = PiReader $ \_ -> return x PiReader f >>= k = PiReader $ \pp -> do a <- f pp let PiReader g = k a in g pp fail = liftBase . fail instance Functor PiReader where fmap = liftM instance MonadBase PiReader where liftBase ba = PiReader $ \_ -> ba withErrCtxt' ecf (PiReader f) = PiReader $ withErrCtxt' ecf . f tryM (PiReader f) = PiReader $ tryM . f instance MonadIO PiReader where liftIO = liftBase . liftIO class MonadBase m => MonadPR m where liftPR :: PiReader a -> m a -- liftPRWith :: (PiReader a -> PiReader a) -> m a -> m a instance MonadPR PiReader where liftPR = id -- liftPRWith = id getPiPathPR :: PiReader PiPath getPiPathPR = PiReader return getPiPath :: MonadPR m => m PiPath getPiPath = liftPR getPiPathPR --------------------------------------------------- -- The real functionality: reading pi files doesPkgExist :: MonadPR m => Name () -> m Bool doesPkgExist n = liftPR $ do path <- pNameToDir n piPath <- getPiPath or <$> mapM (\p -> liftIO $ doesDirectoryExist $ p path) piPath doesTypeExist :: MonadPR m => Name () -> m Bool doesTypeExist n = liftPR $ do path <- tNameToFile n piPath <- getPiPath liftIO $ go piPath path where go [] _ = return False go (p:pis) path = do let fp = p path debugPrint $ "Checking for " ++ fp found <- doesFileExist fp if found then do debugPrint $ "Found " ++ fp return True else go pis path getPkgContents :: MonadPR m => Name () -> m [Ident ()] getPkgContents n = liftPR $ do path <- pNameToDir n piPath <- getPiPath findFirstPkg path piPath where findFirstPkg :: FilePath -> [FilePath] -> PiReader [Ident ()] findFirstPkg _ [] = panic (piReaderModule ++ ".getPkgContents") ("No such package exists - doesPkgExist not called successfully" ++ show n) findFirstPkg path (pip:pips) = do isP <- liftIO $ doesDirectoryExist $ pip path if isP then do files <- liftIO $ getDirectoryContents $ pip path let fnses = map splitExtension files return [ Ident () str | (str, ".pi") <- fnses, not (null str), head str /= '.' ] else findFirstPkg path pips getPiPathContents :: MonadPR m => m ([Ident ()], [Ident ()]) getPiPathContents = do pp <- getPiPath liftIO $ go pp ([],[]) where go :: [FilePath] -> ([Ident ()], [Ident ()]) -> IO ([Ident ()], [Ident ()]) go [] acc = return acc go (p:pis) (ts,ps) = do isDir <- doesDirectoryExist p if isDir then do files <- getDirectoryContents p pkgs <- filterPkgIdentsM files let tys = filterPiIdents files go pis (tys++ts,pkgs++ps) else go pis (ts,ps) getTypeContents :: MonadPR m => Name () -> m (CompilationUnit ()) getTypeContents n = liftPR $ do path <- tNameToFile n piPath <- getPiPath findFirstPi path piPath where findFirstPi :: FilePath -> [FilePath] -> PiReader (CompilationUnit ()) findFirstPi _ [] = panic (piReaderModule ++ ".getTypeContents") ("No such type exists - doesTypeExist not called successfully: " ++ show n) findFirstPi path (pip:pips) = do isT <- liftIO $ doesFileExist $ pip path if isT then do fc <- liftIO $ readFile $ pip path let pRes = parser compilationUnit fc case pRes of Right cu -> return cu Left pe -> fail $ "Parse error in pi file for type " ++ prettyPrint n ++ ":\n" ++ show pe else findFirstPi path pips ---- Helper functions filterPiIdents :: [FilePath] -> [Ident ()] filterPiIdents files = let fnses = map splitExtension files in [ Ident () str | (str, ".pi") <- fnses ] filterPkgIdentsM :: MonadIO m => [FilePath] -> m [Ident ()] filterPkgIdentsM files = liftIO $ do fs <- filterM doesDirectoryExist files return [ Ident () f | f <- fs, head f /= '.' ] pNameToDir :: Name () -> PiReader FilePath pNameToDir (Name _ PName mPre (Ident _ str)) = do pf <- case mPre of Nothing -> return id Just pre -> do fp <- pNameToDir pre return (fp ) return $ pf str pNameToDir (Name _ TName _ _) = fail "Inner types not supported" pNameToDir n = panic (piReaderModule ++ ".pNameToDir") $ show n tNameToFile :: Name () -> PiReader FilePath tNameToFile (Name _ TName mPre (Ident _ str)) = do pf <- case mPre of Nothing -> return id Just pre -> do fp <- pNameToDir pre return (fp ) return $ pf str <.> "pi" tNameToFile n = panic (piReaderModule ++ ".tNameToFile") $ show n