packages feed

paragon-0.1.28: src/Language/Java/Paragon/Monad/PiReader.hs

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 qualified Data.ByteString.Char8 as B

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 [B.ByteString]
getPkgContents n = liftPR $ do
  path <- pNameToDir n
  piPath <- getPiPath
  findFirstPkg path piPath

      where findFirstPkg :: FilePath -> [FilePath] -> PiReader [B.ByteString]
            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 [ B.pack str | (str, ".pi") <- fnses, not (null str), head str /= '.' ]
                      else findFirstPkg path pips

getPiPathContents :: MonadPR m => m ([B.ByteString], [B.ByteString])
getPiPathContents = do
  pp <- getPiPath
  liftIO $ go pp ([],[])

      where go :: [FilePath] -> ([B.ByteString], [B.ByteString]) 
               -> IO ([B.ByteString], [B.ByteString]) 
            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] -> [B.ByteString]
filterPiIdents files =
    let fnses = map splitExtension files
    in [ B.pack str | (str, ".pi") <- fnses ]

filterPkgIdentsM :: MonadIO m => [FilePath] -> m [B.ByteString]
filterPkgIdentsM files = liftIO $ do
    fs <- filterM doesDirectoryExist files    
    return [ B.pack 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 (B.unpack 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 (B.unpack str) <.> "pi"
tNameToFile n = panic (piReaderModule ++ ".tNameToFile") $ show n