-------------------------------------------------------------------------------- module Language.Haskell.Stylish.Parse ( parseModule ) where -------------------------------------------------------------------------------- import Data.List (isPrefixOf, nub) import Data.Maybe (fromMaybe, listToMaybe) import qualified Language.Haskell.Exts as H -------------------------------------------------------------------------------- import Language.Haskell.Stylish.Config import Language.Haskell.Stylish.Step -------------------------------------------------------------------------------- -- | Syntax-related language extensions are always enabled for parsing. Since we -- can't authoritatively know which extensions are enabled at compile-time, we -- should try not to throw errors when parsing any GHC-accepted code. defaultExtensions :: [H.Extension] defaultExtensions = map H.EnableExtension [ H.GADTs , H.HereDocuments , H.KindSignatures , H.NewQualifiedOperators , H.PatternGuards , H.StandaloneDeriving , H.UnicodeSyntax ] -------------------------------------------------------------------------------- -- | Filter out lines which use CPP macros unCpp :: String -> String unCpp = unlines . go False . lines where go _ [] = [] go isMultiline (x : xs) = let isCpp = isMultiline || listToMaybe x == Just '#' nextMultiline = isCpp && not (null x) && last x == '\\' in (if isCpp then "" else x) : go nextMultiline xs -------------------------------------------------------------------------------- -- | Remove shebang lines unShebang :: String -> String unShebang str = let (shebangs, other) = break (not . ("#!" `isPrefixOf`)) (lines str) in unlines $ map (const "") shebangs ++ other -------------------------------------------------------------------------------- -- | If the given string is prefixed with an UTF-8 Byte Order Mark, drop it -- because haskell-src-exts can't handle it. dropBom :: String -> String dropBom ('\xfeff' : str) = str dropBom str = str -------------------------------------------------------------------------------- -- | Abstraction over HSE's parsing parseModule :: Extensions -> Maybe FilePath -> String -> Either String Module parseModule extraExts mfp string = do -- Determine the extensions: those specified in the file and the extra ones let noPrefixes = unShebang . dropBom $ string extraExts' = map H.classifyExtension extraExts (lang, fileExts) = fromMaybe (Nothing, []) $ H.readExtensions noPrefixes exts = nub $ fileExts ++ extraExts' ++ defaultExtensions -- Parsing options... fp = fromMaybe "" mfp mode = H.defaultParseMode { H.extensions = exts , H.fixities = Nothing , H.baseLanguage = case lang of Nothing -> H.baseLanguage H.defaultParseMode Just l -> l } -- Preprocessing processed = if H.EnableExtension H.CPP `elem` exts then unCpp noPrefixes else noPrefixes case H.parseModuleWithComments mode processed of H.ParseOk md -> return md err -> Left $ "Language.Haskell.Stylish.Parse.parseModule: could not parse " ++ fp ++ ": " ++ show err