embeddock-0.1.0.0: src/main.hs
{-# OPTIONS -Wall #-}
import Control.Monad
import Data.List (isInfixOf)
import Data.String.Utils (replace)
import Language.Haskell.Her.HaLay (ready, tokssOut, toksOut, Tok(Com))
import System.Environment
import System.FilePath (splitFileName, splitExtension, (</>), (<.>))
import System.IO
import System.Process (runInteractiveCommand)
import Text.Printf
main :: IO ()
main = do
(srcPath:_:destPath:ppOpts) <- getArgs
src <- readFile srcPath
let
embedKey :: String
runhaskellArgs :: [String]
(embedKey,runhaskellArgs) = case ppOpts of
[] -> ("$", [])
(x:xs) -> (x, xs)
openKey, closeKey :: String
openKey = findFree "Open" 1 "Sesami"
closeKey = findFree "Close" 1 "Sesami"
findFree tag n tag' =
let cand = tag ++ show (n::Integer) ++ tag' in
if cand `isInfixOf` src then findFree tag (3*n) tag'
else cand
parsedSrc :: [[Tok]]
parsedSrc =
map (filter (not . isEmbedPragma)) $
ready srcPath src
isEmbedPragma :: Tok -> Bool
isEmbedPragma (Com str)
| "OPTIONS_GHC" `isInfixOf` str && "embeddock" `isInfixOf` str
= True
| otherwise = False
isEmbedPragma _ = False
(srcDir, srcFn) = splitFileName srcPath
(srcFnBody, srcExt) = splitExtension srcFn
isEmbedLoop = (head srcFn == '.') &&
("_embeddock." `isInfixOf` srcFn)
runnerFn = srcDir </> ("." ++ srcFnBody ++ "_embeddock") <.> srcExt
destContent = tokssOut parsedSrc
quineMain = unlines $ "main = do" :map mkPrinter parsedSrc
mkPrinter :: [Tok] -> String
mkPrinter toks = (" putStr $ " ++ ) $
replace openKey "\"++(" $
replace closeKey ")++\"" $
show $ toksOut $ map seedEmbed toks
where
embeds :: [String]
embeds = toks >>= findEmbed
seedEmbed :: Tok -> Tok
seedEmbed (Com str) = Com $ foldl
(\str e -> replace (printf "%s(%s)" embedKey e)
(printf "%s%s%s" openKey e closeKey)
str)
str embeds
seedEmbed x = x
findEmbed :: Tok -> [String]
findEmbed (Com str) = go str
where
go [] = []
go xss@(_:xs) = try embedKey xss `mplus` go xs
try [] [] = []
try _ [] = []
try (k:ey) (x:xs)
| k==x = try ey xs
| otherwise = []
try [] ('(':xs) = tryParen (1::Int) xs ""
try _ _ = []
tryParen n [] buf
| n <= 0 = [reverse $ drop 1 buf]
| otherwise = []
tryParen n (x:xs) buf
| n <= 0 = [reverse $ drop 1 buf]
| otherwise = let next '(' = n+1
next ')' = n-1
next _ = n
in tryParen (next x) xs (x:buf)
findEmbed _ = []
when (not isEmbedLoop) $ do
print parsedSrc
print embedKey
writeFile runnerFn $ destContent ++ "\n" ++ quineMain
(_, hOut, _, _) <- runInteractiveCommand $
printf "runhaskell %s %s" (unwords runhaskellArgs) runnerFn
hGetContents hOut >>= writeFile destPath