packages feed

derive-2.1.0: Derive/Derivation.hs

module Derive.Derivation(wantDerive, performDerive, writeDerive) where

import System.IO
import System.IO.Unsafe
import Language.Haskell
import Control.Arrow
import Control.Monad
import Data.Maybe
import Data.List
import Derive.Utils
import Derive.Flags
import Data.Derive.All
import Data.Derive.Internal.Derivation
import qualified Data.Map as Map


---------------------------------------------------------------------
-- WHAT DO YOU WANT TO DERIVE

wantDerive :: [Flag] -> Module -> Module -> [Type]
wantDerive flag real mine = nub $ map fromTyParens $ wantDeriveFlag flag decls ++ wantDeriveAnnotation real mine
    where decls = filter isDataDecl $ moduleDecls mine


wantDeriveFlag :: [Flag] -> [DataDecl] -> [Type]
wantDeriveFlag flags decls = [TyApp (tyCon x) d | Derive xs <- flags, x <- xs, d <- declst]
    where declst = [tyApps (tyCon $ dataDeclName d) (map tyVar $ dataDeclVars d) | d <- decls]

wantDeriveAnnotation :: Module -> Module -> [Type]
wantDeriveAnnotation real mine = moduleDerives mine \\ moduleDerives real


moduleDerives :: Module -> [Type]
moduleDerives = concatMap f . moduleDecls 
    where
        f (DataDecl _ _ _ name vars _ deriv) = g name vars deriv
        f (GDataDecl _ _ _ name vars _ _ deriv) = g name vars deriv
        f (DerivDecl _ _ name args) = [TyCon name `tyApps` args]
        f _ = []

        g name vars deriv = [TyCon a `tyApps` (b:bs) | (a,bs) <- deriv]
            where b = TyCon (UnQual name) `tyApps` map (tyVar . prettyPrint) vars


---------------------------------------------------------------------
-- ACTUALLY DERIVE IT

performDerive :: Module -> [Type] -> [String]
performDerive modu = concatMap ((:) "" . f)
    where
        grab = getDecl modu

        f ty = case d ty grab (moduleName modu, grab typ1Name) of
                Left x -> unsafePerformIO $ let res = msg x in hPutStrLn stderr res >> return ["-- " ++ res]
                Right x -> concatMap (lines . prettyPrint) x
            where
                d = derivationOp $ getDerivation clsName
                (cls,typ1:_) = fromTyApps ty
                clsName = prettyPrint cls
                typ1Name = tyRoot typ1
                msg x = "Deriving " ++ prettyPrint ty ++ ": " ++ x


getDecl :: Module -> (String -> Decl)
getDecl modu = \name -> Map.findWithDefault (error $ "Can't find data type definition for: " ++ name) name mp
    where
        mp = Map.fromList $ concatMap f $ moduleDecls modu
        f x@(DataDecl _ _ _ name _ _ _) = [(prettyPrint name, x)]
        f x@(GDataDecl _ _ _ name _ _ _ _) = [(prettyPrint name, x)]
        f x@(TypeDecl _ name _ _) = [(prettyPrint name, x)]
        f _ = []


getDerivation :: String -> Derivation
getDerivation = \name -> Map.findWithDefault (error $ "Don't know how to derive type class: " ++ name) name mp
    where
        mp = Map.fromList $ map (derivationName &&& id) derivations


---------------------------------------------------------------------
-- WRITE IT BACK

writeDerive :: FilePath -> ModuleName -> [Flag] -> [String] -> IO ()
writeDerive file modu flags xs = do
    -- force the output first, ensure that we don't crash half way through
    () <- length (concat xs) `seq` return ()
    
    let append = Append `elem` flags
    let output = [x | Output x <- flags]

    let pre = take 1 ["module " ++ x ++ " where" | Modu x <- flags] ++
              ["import " ++ if null i then prettyPrint modu else i | Import i <- flags]

    when append $ do
        src <- readFile' file
        writeGenerated file xs

    forM output $ \o ->
        writeFile o $ unlines $ pre ++ xs

    when (not append && null output) $
        putStr $ unlines xs