module Derive.Generate(generate) where
import Language.Haskell.Exts
import Data.DeriveDSL
import Derive.Utils
import Control.Monad
import Data.Maybe
import System.FilePath
import System.Directory
import Data.Char
import Data.List
evil = words "TTypeable Uniplate"
generate :: IO ()
generate = do
xs <- getDirectoryContents "src/Data/Derive"
xs <- return $ sort [x | x <- xs, takeExtension x == ".hs", x /= "All.hs", takeBaseName x `notElem` evil]
lis <- mapM generateFile $ map ("src/Data/Derive" </>) xs
let names = map dropExtension xs
n = maximum $ map length names
writeGenerated "src/Data/Derive/All.hs" $
["import Data.Derive." ++ x ++ replicate (4 + n - length x) ' ' ++ "as D" | x <- names] ++
["derivations :: [Derivation]"
,"derivations = [make" ++ concat (intersperse ",make" names) ++ "]"]
writeGenerated "README.md" $ ["-->",""] ++ lis ++ ["","<!--"]
writeGenerated "derive.cabal" $ map (" Data.Derive."++) names
generateFile :: FilePath -> IO String
generateFile file = do
let name = takeBaseName file
putStrLn $ "Generating " ++ name
src <- readSrc file
when (isJust $ srcExample src) $ do
let dsl = fromMaybe (error $ "Couldn't derive example for " ++ name) $
deriveDSL $ fromJust $ srcExample src
writeGenerated file $
[""
,"import Data.Derive.DSL.DSL"
,"import Data.Derive.Internal.Derivation"
,""
,"make" ++ name ++ " :: Derivation"
] ++ (if srcCustom src then
["make" ++ name ++ " = derivationCustomDSL " ++ show name ++ " custom $"]
else
["make" ++ name ++ " = derivationDSL " ++ show name ++ " dsl" ++ name
,""
,"dsl" ++ name ++ " ="
]) ++
map (replicate 4 ' ' ++) (wrap 66 $ show dsl)
let inst = dynamicDSL dsl
instFile = takeDirectory file </> "Instance" </> name <.> "hs"
b <- doesFileExist instFile
if not (srcCustom src) && isJust inst then do
writeGenerated instFile $
["{-# LANGUAGE FlexibleInstances, UndecidableInstances, ScopedTypeVariables #-}"] ++
["","module Data.Derive.Instance." ++ name ++ " where",""] ++
(map prettyPrint $ srcImportStd src) ++
["import Data.Derive.Internal.Instance",""] ++
(map prettyPrint $ fromJust inst) ++ [""]
else when b $
error $ "Previously generated dynamic instance can not be regenerated, " ++ name
let imp = listToMaybe $ srcImport src
return $
"* **[" ++ name ++ "](" ++ instUrl name imp ++ ")**" ++
concat [" - from the library [" ++ pkg ++ "](" ++ pkgUrl pkg ++ ")" | Just imp <- [imp], let pkg = fromMaybe "base" $ importPkg imp]
pkgUrl x = "http://hackage.haskell.org/package/" ++ x
instUrl name Nothing = "http://hackage.haskell.org/packages/archive/derive/latest/doc/html/Data-Derive-" ++ name ++ ".html"
instUrl name (Just x) = "http://hackage.haskell.org/packages/archive/" ++ pkgName ++ "/" ++ pkgVersion ++ "/doc/html/" ++ modu ++ ".html#t%3A" ++ nam
where
(a,b) = break (== '-') $ fromMaybe "base" $ importPkg x
pkgName = a
pkgVersion = if null b then "latest" else tail b
modu = reps '.' '-' $ prettyPrint $ importModule x
nam = case importSpecs x of Just (ImportSpecList _ False (IAbs _ _ y:_)) -> prettyPrint y ; _ -> name
wrap :: Int -> String -> [String]
wrap n = f . lexemes
where
f [] = []
f (x:xs) = [reverse $ dropWhile isSpace $ reverse $ concat $ x:a] ++ f (dropWhile (all isSpace) b)
where (a,b) = thisLine (n - length x) xs
thisLine i [] = ([], [])
thisLine i (x:xs) | j > i = ([], x:xs)
| otherwise = (x:a, b)
where j = length x
(a,b) = thisLine (i - j) xs
lexemes :: String -> [String]
lexemes [] = []
lexemes x = a : lexemes b
where (a,b) = lexeme x
lexeme :: String -> (String, String)
lexeme xs@(x:_) | isAlpha x = span isAlpha xs
lexeme ('\"':xs) = let (a,b) = f xs in ('\"':a,b)
where f ('\\':x:xs) = let (a,b) = f xs in ('\\':x:a,b)
f ('\"':xs) = ("\"",xs)
f (x:xs) = let (a,b) = f xs in (x:a,b)
f [] = ([],[])
lexeme (x:xs) = ([x], xs)