|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
|
{-# LANGUAGE TypeFamilies #-}
|
|
module Main.Rules where
|
|
|
|
import Control.DeepSeq (deepseq)
|
|
import Control.Concurrent
|
|
import Control.Exception
|
|
import Control.Monad
|
|
|
|
import Crypto.Hash.SHA1 as Sha1
|
|
|
|
import qualified Data.ByteString.Lazy as Lbs
|
|
import qualified Data.Text.Lazy.IO as Lt
|
|
import Data.ByteString (ByteString)
|
|
import qualified Data.Text as T
|
|
import Data.Foldable (for_)
|
|
|
|
import Development.Shake.FilePath
|
|
import Development.Shake.Classes
|
|
import Development.Shake
|
|
|
|
import Errors
|
|
|
|
import Frontend.Lexer.Wrapper (runAlex)
|
|
import Frontend.Autogen.Parser
|
|
import Frontend.Parser.Posn
|
|
import Frontend.Syntax.Var
|
|
import Frontend.Syntax
|
|
|
|
import GHC.Generics (Generic)
|
|
|
|
import Main.Queries
|
|
|
|
import Rename.Types (FqVar, ModuleId(..), RenamedMod)
|
|
import qualified Rename.Types as Rv
|
|
import Rename.Rename
|
|
|
|
import System.IO
|
|
|
|
import Text.Show.Pretty
|
|
|
|
findModuleOracle :: Rules ()
|
|
findModuleOracle = void $ addOracleCache find where
|
|
find :: AhcModuleFilepath -> Action String
|
|
find (AhcModuleFilepath our_fp (ModId id (Posn sl sc) pe)) = go "" id where
|
|
go fp xs | T.null xs || not (T.singleton '.' `T.isInfixOf` xs) = do
|
|
fp <- pure (fp </> T.unpack xs <.> "hs")
|
|
t <- doesFileExist fp
|
|
unless t . liftIO . throwIO $
|
|
emptyError { errorMessage = "file does not exist:"
|
|
, errorFilename = our_fp
|
|
, errorBegin = Posn sl (sc + length fp)
|
|
, errorEnd = pe
|
|
}
|
|
pure fp
|
|
|
|
go fp xs = do
|
|
(x, xs) <- pure $ T.span (/= '.') xs
|
|
ex <- doesDirectoryExist (T.unpack x)
|
|
unless ex . liftIO . throwIO $
|
|
emptyError { errorMessage = "directory in module path does not exist:"
|
|
, errorFilename = our_fp
|
|
, errorBegin = Posn sl (sc + length fp)
|
|
, errorEnd = Posn sl (sc + length fp + T.length x)
|
|
}
|
|
go (fp </> T.unpack x) xs
|
|
|
|
find _ = undefined
|
|
|
|
parserOracle :: Rules (String -> Action (FeModule ParsedVar))
|
|
parserOracle = fmap (. AhcParsedModule) $ addOracleCache (parse . ahcpmFilePath) where
|
|
parse :: String -> Action (FeModule ParsedVar)
|
|
parse fpath = do
|
|
need [fpath]
|
|
fileContents <- liftIO $ Lt.readFile fpath
|
|
|
|
let mod = runAlex fpath fileContents parseMod
|
|
() <- liftIO . evaluate $ rnf mod
|
|
|
|
case mod of
|
|
Left e -> liftIO $ throwIO e
|
|
Right x -> pure x
|
|
|
|
renamerOracle :: Rules (String -> Action RenamedMod)
|
|
renamerOracle = fmap (. AhcRenamedModule) $ addOracleCache (rename . ahcrnFilePath) where
|
|
rename :: String -> Action RenamedMod
|
|
rename fpath = do
|
|
mod <- askOracle (AhcParsedModule fpath) :: Action (FeModule ParsedVar)
|
|
hash <- askOracle (AhcModuleHash fpath)
|
|
mvar <- liftIO $ newMVar mempty
|
|
let
|
|
modid = ModuleId { Rv.moduleName = varId $ Frontend.Syntax.moduleName mod
|
|
, moduleHash = hash
|
|
, moduleFp = fpath
|
|
}
|
|
rnctx = RenameCtx { rcOurModId = modid
|
|
, rcOurFilePath = fpath
|
|
, rcScope = mempty
|
|
, rcThisModule = mvar
|
|
, rcSpanStart = Posn 1 1
|
|
, rcSpanEnd = Posn 1 1
|
|
}
|
|
runRename (renameModule mod) rnctx
|
|
|
|
hashOracle :: Rules (String -> Action ByteString)
|
|
hashOracle = fmap (. AhcModuleHash) . addOracleCache $ \(AhcModuleHash fp) -> do
|
|
need [fp]
|
|
fileContents <- liftIO $ Lbs.readFile fp
|
|
pure (Sha1.hashlazy fileContents)
|
|
|
|
compilerRules :: [String] -> Rules ()
|
|
compilerRules wanted_mods = do
|
|
findModuleOracle
|
|
parser <- parserOracle
|
|
hash <- hashOracle
|
|
rename <- renamerOracle
|
|
|
|
action $ do
|
|
for_ wanted_mods $ \path -> do
|
|
ast <- rename path
|
|
liftIO $ pPrint ast
|