module Parse ( DocTest(..) , Interaction(..) , getDocTests -- * exported for testing , parse ) where import Data.Char (isSpace) import Data.List import Data.Maybe (fromMaybe) import Extract data DocTest = DocExample { moduleName :: String , interactions :: [Interaction] } deriving (Eq, Show) data Interaction = Interaction { expression :: String -- ^ example expression , result :: [String] -- ^ expected result } deriving (Eq, Show) -- | -- Extract 'DocTest's from all given modules and all modules included by the -- given modules. getDocTests :: [String] -- ^ List of GHC flags -> [String] -- ^ File or module names -> IO [DocTest] -- ^ Extracted 'DocTest's getDocTests flags modules = do mods <- extract flags modules return (concatMap moduleToDocTest mods) -- | Convert a `Module` to a list of `DocTest`s. moduleToDocTest :: Module -> [DocTest] moduleToDocTest (Module name docs) = (map (DocExample name) . filter (not . null) . map parse) docs -- | Extract all interactions from given Haddock documentation. parse :: String -> [Interaction] parse input = go (lines input) where isPrompt = isPrefixOf ">>>" . dropWhile isSpace isBlankLine = null . dropWhile isSpace isEndOfInteraction x = isPrompt x || isBlankLine x go :: [String] -> [Interaction] go xs = case dropWhile (not . isPrompt) xs of prompt:rest -> let (ys,zs) = break isEndOfInteraction rest in toInteraction prompt ys : go zs _ -> [] -- | Create an `Interaction`, strip superfluous whitespace as appropriate. toInteraction :: String -> [String] -> Interaction toInteraction x xs = Interaction (strip $ drop 3 e) -- we do not care about leading and trailing -- whitespace in expressions, so drop them result_ where -- 1. drop trailing whitespace from the prompt, remember the prefix (prefix, e) = span isSpace x -- 2. drop, if possible, the exact same sequence of whitespace -- characters from each result line -- -- 3. interpret lines that only contain the string "" as an -- empty line result_ = map (substituteBlankLine . tryStripPrefix prefix) xs where tryStripPrefix pre ys = fromMaybe ys $ stripPrefix pre ys substituteBlankLine "" = "" substituteBlankLine line = line -- | Remove leading and trailing whitespace. strip :: String -> String strip = dropWhile isSpace . reverse . dropWhile isSpace . reverse