module Data.Origami.Internal.Build(
buildFolds,
buildFoldsDryRun,
BuildErr,
buildFoldFamilyMaybe
) where
import Control.Applicative(Applicative(..))
import Control.Lens hiding (Fold)
import Control.Monad.Error(Error(..), ErrorT(..), MonadError(..))
import Control.Monad.RWS(MonadReader(..), MonadState(..), MonadWriter(..),
RWST(..), evalRWST, gets, lift, liftM, liftM2, liftM3, modify, unless,
when)
import Data.Bitraversable(Bitraversable)
import qualified Data.Data as D
import Data.List(intercalate)
import qualified Data.Map as M
import Data.Origami.Internal.Fold(foldFoldFamily)
import Data.Origami.Internal.FoldFamily
import Data.Origami.Internal.Pretty(prettyFold)
import Data.Origami.Internal.TH(duplicateCtorNames, mkFoldDecs)
import Data.Origami.Internal.THUtils(unAppTs, upperName)
import Data.Origami.Internal.Trifunctor(Tritraversable)
import qualified Data.Set as S
import Language.Haskell.TH
import Language.Haskell.TH.Quote(dataToExpQ)
buildFoldsDryRun :: [Name] -> [Name] -> [Name] -> Q [Dec]
buildFoldsDryRun rts functs atoms = do
ff <- buildFoldFamily rts functs atoms
runIO $ do
print $ foldFoldFamily prettyFold ff
print $ ppr $ mkFoldDecs ff
return []
buildFolds :: [Name]
-> [Name]
-> [Name]
-> Q [Dec]
buildFolds rts functs atoms = do
ff <- buildFoldFamily rts functs atoms
return $ mkFoldDecs ff
buildFoldFamily :: [Name] -> [Name] -> [Name] -> Q FoldFamily
buildFoldFamily rts functs atoms = do
e <- runBuild $ buildFoldFamilyMB rts functs atoms
case e of
Left err -> fail $ show err
Right ff -> return ff
buildFoldFamilyMaybe :: [Name] -> [Name] -> [Name] -> Q Exp
buildFoldFamilyMaybe rts functs atoms = do
e <- recover (return $ Left ErrThrownInQ)
$ runBuild
$ buildFoldFamilyMB rts functs atoms
dataToExpQ (const Nothing) e
buildFoldFamilyMB :: forall m . MonadBuild m
=> [Name] -> [Name] -> [Name] -> m FoldFamily
buildFoldFamilyMB rts functs atoms = do
((), w) <- getData runDfsM
case processData w of
Left err -> throwErr err
Right ff -> return ff
where
runDfsM :: m ()
runDfsM = do
mapM_ see atoms
mapM_ visitNm rts
visitNm :: Name -> m ()
visitNm nm = do
s <- seen nm
unless s $ withStackTop nm $ do
see nm
dcs <- getDataCases nm
putDataTy nm dcs
getDataCases :: Name -> m [DataCase]
getDataCases nm = do
info <- reifyTypeName nm
case info of
TyConI dec -> getDataCasesFromDec nm dec
_ -> throwErrWithStack $ ErrReify nm info
getDataCasesFromDec :: Name -> Dec -> m [DataCase]
getDataCasesFromDec nm dec = case dec of
DataD _ nm' [] cons' _
-> getDataCasesFromDataD nm' cons'
DataD {} -> throwErrWithStack $ ErrParamType (pretty dec)
NewtypeD _ nm' [] con _
-> getDataCasesFromDataD nm' [con]
NewtypeD {} -> throwErrWithStack $ ErrParamType (pretty dec)
TySynD nm' [] ty
-> withStackTop nm' $ case unAppTs ty of
[ConT _nm] -> throwErrWithStack $ ErrUnimpl $ concat [
"getDataCasesFromDec ", pretty nm, " ", pretty dec]
_ -> throwErrWithStack $ ErrParamType (pretty dec)
TySynD nm' _ _
-> withStackTop nm'
$ throwErrWithStack
$ ErrParamTypeSyn (pretty dec)
_ -> throwErrWithStack $ ErrReify' nm (pretty dec)
getDataCasesFromDataD :: Name -> [Con] -> m [DataCase]
getDataCasesFromDataD nm' cons' = if null cons'
then throwErrWithStack $ ErrEmptyData nm'
else mapM getDataCasesFromCon cons'
getDataCasesFromCon :: Con -> m DataCase
getDataCasesFromCon con = case con of
NormalC nm' sts -> do
dfs <- mapM (getDataFieldFromType . snd) sts
return $ DataCase nm' dfs
RecC nm' vsts -> do
dfs <- mapM (getDataFieldFromType . thd3) vsts
return $ DataCase nm' dfs
InfixC _ nm' _ -> throwErrWithStack $ ErrInfixCtor nm'
ForallC {} -> throwErrWithStack
$ ErrUnsupported
"Universally quanitified constructors"
where
thd3 :: (a, b, c) -> c
thd3 (_, _,c) = c
getDataFieldFromType :: Type -> m DataField
getDataFieldFromType t = case unAppTs t of
[ConT nm'] -> getDataFieldFromConstructor nm'
[ConT nm', t1] -> getDataFieldFromFunctApp nm' t1
[ListT, t1] -> getDataFieldFromFunctApp ''[] t1
[ConT nm', t1, t2] -> getDataFieldFromBifunctApp nm' t1 t2
[TupleT 2, t1, t2] -> getDataFieldFromBifunctApp ''(,) t1 t2
[ConT nm', t1, t2, t3] -> getDataFieldFromTrifunctApp nm' t1 t2 t3
[TupleT 3, t1, t2, t3] -> getDataFieldFromTrifunctApp ''(,,) t1 t2 t3
(ConT nm' : _) -> do
info <- reifyTypeName nm'
case info of
TyConI dec -> case dec of
DataD {} -> throwErrWithStack $ ErrParamType (pretty dec)
NewtypeD {}
-> throwErrWithStack $ ErrParamType (pretty dec)
TySynD nm _ _
-> withStackTop nm
$ throwErrWithStack
$ ErrParamTypeSyn (pretty dec)
_ -> throwErrWithStack $ ErrReify' nm' (pretty dec)
_ -> throwErrWithStack $ ErrReify nm' info
_ -> throwErrWithStack
$ ErrUnimpl ("getDataFieldFromType " ++ pretty t)
getDataFieldFromConstructor :: Name -> m DataField
getDataFieldFromConstructor nm' = if nm' `elem` atoms
then return $ Atomic $ Ty nm'
else do
mNmTy <- getTypeSynDef nm'
case mNmTy of
Just (nm'', t) -> withStackTop nm'' $ getDataFieldFromType t
Nothing -> do
visitNm nm'
return $ Nonatomic (Ty nm')
getDataFieldFromFunctApp :: Name -> Type -> m DataField
getDataFieldFromFunctApp nm' t = do
assertInFunct nm'
assertClassMembership nm' ''Traversable
liftM (Funct nm') (getDataFieldFromType t)
getDataFieldFromBifunctApp :: Name -> Type -> Type -> m DataField
getDataFieldFromBifunctApp nm' t1 t2 = do
assertInFunct nm'
assertClassMembership nm' ''Bitraversable
liftM2 (Bifunct nm') (getDataFieldFromType t1)
(getDataFieldFromType t2)
getDataFieldFromTrifunctApp :: Name -> Type -> Type -> Type -> m DataField
getDataFieldFromTrifunctApp nm' t1 t2 t3 = do
assertInFunct nm'
assertClassMembership nm' ''Tritraversable
liftM3 (Trifunct nm') (getDataFieldFromType t1)
(getDataFieldFromType t2)
(getDataFieldFromType t3)
getTypeSynDef :: Name -> m (Maybe (Name, Type))
getTypeSynDef nm' = do
info <- reifyTypeName nm'
case info of
TyConI dec -> case dec of
TySynD nm tvbs t -> withStackTop nm $ if null tvbs
then return $ Just (nm, t)
else throwErrWithStack $ ErrParamTypeSyn (pretty dec)
_ -> return Nothing
_ -> return Nothing
assertInFunct :: Name -> m ()
assertInFunct nm' = unless (nm' `elem` functs)
$ throwErrWithStack $ ErrNoFunct nm'
assertClassMembership :: Name -> Name -> m ()
assertClassMembership nm' clsNm
| (nm', clsNm) == (''[], ''Traversable) = return ()
| (nm', clsNm) == (''(,), ''Bitraversable) = return ()
| (nm', clsNm) == (''(,,), ''Tritraversable) = return ()
| otherwise = do
info <- reifyTypeName clsNm
case info of
ClassI _dec instances -> do
noInst <- anyM (matchingInst info) instances
unless noInst $ throwErrWithStack $ ErrNoInstance clsNm nm'
_ -> throwErrWithStack $ ErrNoClass clsNm
where
matchingInst :: Info -> InstanceDec -> m Bool
matchingInst info dec = case dec of
InstanceD _ (AppT _ (ConT nm'')) _ -> return $ nm' == nm''
DataInstD _ nm'' [] _ _ -> return $ nm' == nm''
NewtypeInstD {} -> throwErrWithStack
$ ErrReifyUnimpl nm' "NewtypeInstD" info
TySynInstD {} -> throwErrWithStack
$ ErrReifyUnimpl nm' "TySynInstD" info
_ -> return False
anyM :: (a -> m Bool) -> [a] -> m Bool
anyM _ [] = return False
anyM p (a : as) = do
b <- p a
if b
then return True
else anyM p as
type Stack = [Name]
type DecDoc = String
data BuildErr = ErrDupCtors (S.Set String)
| ErrEmptyData Name Stack
| ErrEmptyFold
| ErrInfixCtor Name Stack
| ErrMonadFail String
| ErrNoClass Name Stack
| ErrNoCtor Name Stack
| ErrNoFunct Name Stack
| ErrNoInstance Name Name Stack
| ErrParamType DecDoc Stack
| ErrParamTypeSyn DecDoc Stack
| ErrReify Name Info Stack
| ErrReify' Name DecDoc Stack
| ErrReifyUnimpl Name String Info Stack
| ErrThrownInQ
| ErrUnimpl String Stack
| ErrUnsupported String Stack
deriving (D.Data, D.Typeable)
instance Show BuildErr where
show (ErrDupCtors ctors) = concat [
"Different types use the same constructor name(s): ",
intercalate ", " (map show $ S.toList ctors),
"."]
show (ErrEmptyData nm stk)
= showStk stk (pretty nm ++ " has no constructors.")
show ErrEmptyFold
= "No constructors are used. The resulting fold would be empty."
show (ErrInfixCtor nm stk)
= showStk stk
$ concat ["Infix constructors like (", pretty nm, ") are not yet supported."]
show (ErrMonadFail msg) = msg
show (ErrNoClass nm stk)
= showStk stk
$ concat ["Class ", pretty nm, " is not visible at the splice."]
show (ErrNoCtor nm stk)
= showStk stk
$ concat ["Constructor ",
pretty nm,
" is not visible at the splice."]
show (ErrNoFunct nm stk)
= showStk stk $ concat ["Type ",
pretty nm,
" is used in functor position but",
" is not declared in the splice."]
show (ErrNoInstance cls nm stk)
= showStk stk $ concat ["There is no instance of ",
pretty cls,
" ",
pretty nm,
" visible at the splice."]
show (ErrParamType decDoc stk)
= showStk stk $ concat [decDoc,
" has parameters, ",
"which is not yet supported."]
show (ErrParamTypeSyn decDoc stk)
= showStk stk $ concat [decDoc,
" has parameters, ",
"which is not yet supported."]
show (ErrReify nm info stk)
= showStk stk $ concat ["reify ",
pretty nm,
"returned non-type Info: ",
pretty info,
"."]
show (ErrReify' nm decDoc stk)
= showStk stk $ concat ["reify ",
pretty nm,
"returned Info with bad declaration: ",
decDoc,
"."]
show (ErrReifyUnimpl nm tag info stk)
= showStk stk $ concat ["Not handling Decs of type ",
tag,
" while looking for instances for ",
pretty nm,
" in ",
pretty info,
"."]
show ErrThrownInQ = "Unknown error thrown in Q monad."
show (ErrUnimpl msg stk) = showStk stk (msg ++ " unimplemented.")
show (ErrUnsupported msg stk) = showStk stk (msg ++ " not yet supported.")
showStk :: Stack -> String -> String
showStk stk msg = concat ["Error while processing ",
intercalate " <= " $ map (show . pretty) stk,
":\n",
msg]
type Data = (Name, [DataCase])
processData :: [Data] -> Either BuildErr FoldFamily
processData data' = do
when (null data') $ Left ErrEmptyFold
let ff = FoldFamily [DataTy ws dcs
| (ws, dcs) <- M.toList $ M.fromList data']
let dupCtors = duplicateCtorNames ff
unless (S.null dupCtors) $ Left $ ErrDupCtors dupCtors
return ff
class (Functor m, Applicative m, Monad m) => MonadBuild m where
getData :: m a -> m (a, [Data])
getStack :: m Stack
putDataTy :: Name -> [DataCase] -> m ()
reifyTypeName :: Name -> m Info
see :: Name -> m ()
seen :: Name -> m Bool
throwErr :: BuildErr -> m a
withStackTop :: Name -> m a -> m a
throwErrWithStack :: MonadBuild m => (Stack -> BuildErr) -> m a
throwErrWithStack err = do
stk <- getStack
throwErr $ err stk
newtype Build a = Build {
unB :: ErrorT BuildErr (RWST Stack [Data] (S.Set Name) Q) a
}
deriving (Functor, Applicative, Monad,
MonadError BuildErr,
MonadReader Stack,
MonadState (S.Set Name),
MonadWriter [Data])
instance Error BuildErr where
strMsg = ErrMonadFail
instance MonadBuild Build where
getData = listen
getStack = ask
putDataTy ws dcs = tell [(ws, dcs)]
reifyTypeName = reifyTypeName'
see = modify . S.insert
seen = gets . S.member
throwErr = throwError
withStackTop ws = local (ws:)
runBuild :: Build a -> Q (Either BuildErr a)
runBuild m = liftM fst $ evalRWST (runErrorT $ unB m) [] S.empty
reifyTypeName' :: Name -> Build Info
reifyTypeName' nm = do
let nm' = mkName $ upperName nm
mInfo <- liftQ $ recover (return Nothing) (liftM Just $ reify nm')
case mInfo of
Nothing -> throwErrWithStack $ ErrNoCtor nm
Just info -> return info
where
liftQ :: Q a -> Build a
liftQ = Build . lift . lift
pretty :: Ppr a => a -> String
pretty = show . ppr