{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ViewPatterns #-}
module Language.Haskell.GHC.ExactPrint.Parsers (
Parser
, withDynFlags
, CppOptions(..)
, defaultCppOptions
, parseModule
, parseModuleFromString
, parseModuleWithOptions
, parseModuleWithCpp
, parseExpr
, parseImport
, parseType
, parseDecl
, parsePattern
, parseStmt
, parseWith
, ghcWrapper
, initDynFlags
, initDynFlagsPure
, parseModuleFromStringInternal
, parseModuleApiAnnsWithCpp
, parseModuleApiAnnsWithCppInternal
, postParseTransform
) where
import Language.Haskell.GHC.ExactPrint.Annotate
import Language.Haskell.GHC.ExactPrint.Delta
import Language.Haskell.GHC.ExactPrint.Preprocess
import Language.Haskell.GHC.ExactPrint.Types
import Control.Monad.RWS
#if __GLASGOW_HASKELL__ > 806
import Data.Data (Data)
#endif
import GHC.Paths (libdir)
import qualified ApiAnnotation as GHC
import qualified DynFlags as GHC
import qualified FastString as GHC
import qualified GHC as GHC hiding (parseModule)
import qualified HeaderInfo as GHC
import qualified Lexer as GHC
import qualified MonadUtils as GHC
import qualified Outputable as GHC
import qualified Parser as GHC
import qualified SrcLoc as GHC
import qualified StringBuffer as GHC
#if __GLASGOW_HASKELL__ <= 710
import qualified OrdList as OL
#else
import qualified GHC.LanguageExtensions as LangExt
#endif
import qualified Data.Map as Map
{-# ANN module "HLint: ignore Eta reduce" #-}
{-# ANN module "HLint: ignore Redundant do" #-}
{-# ANN module "HLint: ignore Reduce duplication" #-}
#if __GLASGOW_HASKELL__ > 806
parseWith :: (Data (GHC.SrcSpanLess w), Annotate w, GHC.HasSrcSpan w)
=> GHC.DynFlags
-> FilePath
-> GHC.P w
-> String
-> Either (GHC.SrcSpan, String) (Anns, w)
#else
parseWith :: Annotate w
=> GHC.DynFlags
-> FilePath
-> GHC.P (GHC.Located w)
-> String
-> Either (GHC.SrcSpan, String) (Anns, GHC.Located w)
#endif
parseWith dflags fileName parser s =
case runParser parser dflags fileName s of
#if __GLASGOW_HASKELL__ >= 804
GHC.PFailed _ ss m -> Left (ss, GHC.showSDoc dflags m)
#else
GHC.PFailed ss m -> Left (ss, GHC.showSDoc dflags m)
#endif
GHC.POk (mkApiAnns -> apianns) pmod -> Right (as, pmod)
where as = relativiseApiAnns pmod apianns
runParser :: GHC.P a -> GHC.DynFlags -> FilePath -> String -> GHC.ParseResult a
runParser parser flags filename str = GHC.unP parser parseState
where
location = GHC.mkRealSrcLoc (GHC.mkFastString filename) 1 1
buffer = GHC.stringToStringBuffer str
parseState = GHC.mkPState flags buffer location
withDynFlags :: (GHC.DynFlags -> a) -> IO a
withDynFlags action = ghcWrapper $ do
dflags <- GHC.getSessionDynFlags
void $ GHC.setSessionDynFlags dflags
return (action dflags)
parseFile :: GHC.DynFlags -> FilePath -> String -> GHC.ParseResult (GHC.Located (GHC.HsModule GhcPs))
parseFile = runParser GHC.parseModule
type Parser a = GHC.DynFlags -> FilePath -> String
-> Either (GHC.SrcSpan, String)
(Anns, a)
parseExpr :: Parser (GHC.LHsExpr GhcPs)
parseExpr df fp = parseWith df fp GHC.parseExpression
parseImport :: Parser (GHC.LImportDecl GhcPs)
parseImport df fp = parseWith df fp GHC.parseImport
parseType :: Parser (GHC.LHsType GhcPs)
parseType df fp = parseWith df fp GHC.parseType
parseDecl :: Parser (GHC.LHsDecl GhcPs)
#if __GLASGOW_HASKELL__ <= 710
parseDecl df fp = parseWith df fp (head . OL.fromOL <$> GHC.parseDeclaration)
#else
parseDecl df fp = parseWith df fp GHC.parseDeclaration
#endif
parseStmt :: Parser (GHC.ExprLStmt GhcPs)
parseStmt df fp = parseWith df fp GHC.parseStatement
parsePattern :: Parser (GHC.LPat GhcPs)
parsePattern df fp = parseWith df fp GHC.parsePattern
parseModule
:: FilePath -> IO (Either (GHC.SrcSpan, String) (Anns, GHC.ParsedSource))
parseModule = parseModuleWithCpp defaultCppOptions normalLayout
parseModuleFromString
:: FilePath
-> String
-> IO (Either (GHC.SrcSpan, String) (Anns, GHC.ParsedSource))
parseModuleFromString fp s = ghcWrapper $ do
dflags <- initDynFlagsPure fp s
return $ parseModuleFromStringInternal dflags fp s
parseModuleFromStringInternal
:: GHC.DynFlags
-> FilePath
-> String
-> Either (GHC.SrcSpan, String) (Anns, GHC.ParsedSource)
parseModuleFromStringInternal dflags fileName str =
let (str1, lp) = stripLinePragmas str
res = case runParser GHC.parseModule dflags fileName str1 of
#if __GLASGOW_HASKELL__ >= 804
GHC.PFailed _ ss m -> Left (ss, GHC.showSDoc dflags m)
#else
GHC.PFailed ss m -> Left (ss, GHC.showSDoc dflags m)
#endif
GHC.POk x pmod -> Right $ (mkApiAnns x, lp, dflags, pmod)
in postParseTransform res normalLayout
parseModuleWithOptions :: DeltaOptions
-> FilePath
-> IO (Either (GHC.SrcSpan, String)
(Anns, GHC.ParsedSource))
parseModuleWithOptions opts fp =
parseModuleWithCpp defaultCppOptions opts fp
parseModuleWithCpp
:: CppOptions
-> DeltaOptions
-> FilePath
-> IO (Either (GHC.SrcSpan, String) (Anns, GHC.ParsedSource))
parseModuleWithCpp cpp opts fp = do
res <- parseModuleApiAnnsWithCpp cpp fp
return $ postParseTransform res opts
parseModuleApiAnnsWithCpp
:: CppOptions
-> FilePath
-> IO
( Either
(GHC.SrcSpan, String)
(GHC.ApiAnns, [Comment], GHC.DynFlags, GHC.ParsedSource)
)
parseModuleApiAnnsWithCpp cppOptions file = ghcWrapper $ do
dflags <- initDynFlags file
parseModuleApiAnnsWithCppInternal cppOptions dflags file
ghcWrapper :: GHC.Ghc a -> IO a
ghcWrapper =
GHC.defaultErrorHandler GHC.defaultFatalMessager GHC.defaultFlushOut
. GHC.runGhc (Just libdir)
parseModuleApiAnnsWithCppInternal
:: GHC.GhcMonad m
=> CppOptions
-> GHC.DynFlags
-> FilePath
-> m
( Either
(GHC.SrcSpan, String)
(GHC.ApiAnns, [Comment], GHC.DynFlags, GHC.ParsedSource)
)
parseModuleApiAnnsWithCppInternal cppOptions dflags file = do
#if __GLASGOW_HASKELL__ <= 710
let useCpp = GHC.xopt GHC.Opt_Cpp dflags
#else
let useCpp = GHC.xopt LangExt.Cpp dflags
#endif
(fileContents, injectedComments, dflags') <-
if useCpp
then do
(contents,dflags1) <- getPreprocessedSrcDirect cppOptions file
cppComments <- getCppTokensAsComments cppOptions file
return (contents,cppComments,dflags1)
else do
txt <- GHC.liftIO $ readFileGhc file
let (contents1,lp) = stripLinePragmas txt
return (contents1,lp,dflags)
return $
case parseFile dflags' file fileContents of
#if __GLASGOW_HASKELL__ >= 804
GHC.PFailed _ ss m -> Left $ (ss, (GHC.showSDoc dflags m))
#else
GHC.PFailed ss m -> Left $ (ss, (GHC.showSDoc dflags m))
#endif
GHC.POk (mkApiAnns -> apianns) pmod ->
Right $ (apianns, injectedComments, dflags', pmod)
postParseTransform
:: Either a (GHC.ApiAnns, [Comment], GHC.DynFlags, GHC.ParsedSource)
-> DeltaOptions
-> Either a (Anns, GHC.ParsedSource)
postParseTransform parseRes opts = either Left mkAnns parseRes
where
mkAnns (apianns, cs, _, m) =
Right (relativiseApiAnnsWithOptions opts cs m apianns, m)
initDynFlags :: GHC.GhcMonad m => FilePath -> m GHC.DynFlags
initDynFlags file = do
dflags0 <- GHC.getSessionDynFlags
src_opts <- GHC.liftIO $ GHC.getOptionsFromFile dflags0 file
(dflags1, _, _) <- GHC.parseDynamicFilePragma dflags0 src_opts
let dflags2 = dflags1 `GHC.gopt_set` GHC.Opt_KeepRawTokenStream
(dflags3, _, _) <- GHC.parseDynamicFlagsCmdLine
dflags2
[GHC.noLoc "-hide-all-packages"]
_ <- GHC.setSessionDynFlags dflags3
return dflags3
initDynFlagsPure :: GHC.GhcMonad m => FilePath -> String -> m GHC.DynFlags
initDynFlagsPure fp s = do
dflags0 <- GHC.getSessionDynFlags
let pragmaInfo = GHC.getOptions dflags0 (GHC.stringToStringBuffer $ s) fp
(dflags1, _, _) <- GHC.parseDynamicFilePragma dflags0 pragmaInfo
let dflags2 = dflags1 `GHC.gopt_set` GHC.Opt_KeepRawTokenStream
(dflags3, _, _) <- GHC.parseDynamicFlagsCmdLine
dflags2
[GHC.noLoc "-hide-all-packages"]
_ <- GHC.setSessionDynFlags dflags3
return dflags3
mkApiAnns :: GHC.PState -> GHC.ApiAnns
mkApiAnns pstate
= ( Map.fromListWith (++) . GHC.annotations $ pstate
, Map.fromList ((GHC.noSrcSpan, GHC.comment_q pstate) : GHC.annotations_comments pstate))