module Data.Cursor.CLASE.Gen.Util where
import Control.Monad
import Data.Map (Map)
import Data.Maybe
import Language.Haskell.TH
import qualified Data.Map as Map
data DataType = DataType {
dtName :: Name,
dtCtrs :: [Constructor]
}
| WrapsList {
dtName :: Name,
dtConName :: Name,
dtChildName :: Name
}
data Constructor = Ctr {
ctrName :: Name,
ctrKids :: [Child]
}
data Child
= NonNavigable { childType :: Name }
| Navigable { childType :: Name }
isWrapsList :: DataType -> Bool
isWrapsList (WrapsList _ _ _) = True
isWrapsList _ = False
constructors :: DataType -> [Constructor]
constructors d@(DataType {}) = dtCtrs d
constructors (WrapsList _ conName kidName) = [Ctr conName [Navigable kidName]]
extractChildren :: DataType -> [Name]
extractChildren (DataType _ ctrs) = map ctrName ctrs
extractChildren (WrapsList _ cn _) = [cn]
isNavigable :: Child -> Bool
isNavigable (Navigable _) = True
isNavigable _ = False
buildMap :: [Name] -> Q (Map Name DataType)
buildMap okNames = do
reifiedInfos <- mapM reify okNames
let dts = map infoToDataType reifiedInfos
return . Map.fromList . zip okNames $ dts
where
infoToDataType :: Info -> DataType
infoToDataType info
| isSoloCtr &&
onlyOneTypeInCtr &&
(extractTypeNoList soloType) `elem` okNames &&
isList soloType = WrapsList name soloCtrName soloNonListType
| otherwise = DataType name (map mkCtr ctrs)
where
ctrs = extractConstructors info
name = getInfoName info
mkCtr :: Con -> Constructor
mkCtr con = Ctr conName (map mkChild children)
where
children = extractChildTypesRaw $ con
mkChild :: Type -> Child
mkChild n
| (extractTypeNoList n) `elem` okNames = Navigable (extractTypeNoList n)
| otherwise = NonNavigable (extractType n)
conName = getConName con
isSoloCtr = length ctrs == 1
headCtrsTypes = extractChildTypesRaw . head $ ctrs
soloCtrName = getConName . head $ ctrs
onlyOneTypeInCtr = length headCtrsTypes == 1
soloType = head headCtrsTypes
soloNonListType = extractTypeNoList soloType
data ContextCtr
= ListCC {
ctxCtrName :: String,
ctxCtrTypeFrom :: Name,
ctxCtrTypeTo :: Name,
ctxCtrCtrTo :: Name
}
| NormalCC {
ctxCtrName :: String,
ctxCtrOffset_ :: Maybe Int,
ctxCtrTypesBefore :: [Name],
ctxCtrTypesAfter :: [Name],
ctxCtrTypeFrom :: Name,
ctxCtrTypeTo :: Name,
ctxCtrCtrTo :: Name
}
deriving (Eq, Show)
numCCArgs :: ContextCtr -> Int
numCCArgs cc
| ctxCtrIsList cc = 2 + val
| otherwise = val
where
val = length (ctxCtrArgsBefore cc) + length (ctxCtrArgsAfter cc)
downCtrName :: ContextCtr -> String
downCtrName cc = "M" ++ ctr ++ "To" ++ typ ++ moffset
where
ctr = nameBase . ctxCtrCtrTo $ cc
typ = nameBase . ctxCtrTypeFrom $ cc
moffset = maybe "" show . ctxCtrOffset $ cc
upCtrNameShown :: ContextCtr -> String
upCtrNameShown cc = mupctrshown
where
mupctrshown = "M" ++ typ ++ moffset ++ "To" ++ ctr
ctr = nameBase . ctxCtrCtrTo $ cc
typ = nameBase . ctxCtrTypeFrom $ cc
moffset = maybe "" show . ctxCtrOffset $ cc
ctxCtrOffset :: ContextCtr -> Maybe Int
ctxCtrOffset (ListCC {}) = Nothing
ctxCtrOffset x = ctxCtrOffset_ x
ctxCtrIsList :: ContextCtr -> Bool
ctxCtrIsList (ListCC {}) = True
ctxCtrIsList _ = False
ctxCtrArgsBefore :: ContextCtr -> [String]
ctxCtrArgsBefore cc
| ctxCtrIsList cc = []
| otherwise = map nameBase . ctxCtrTypesBefore $ cc
ctxCtrArgsAfter :: ContextCtr -> [String]
ctxCtrArgsAfter cc
| ctxCtrIsList cc = []
| otherwise = map nameBase . ctxCtrTypesAfter $ cc
buildContextCtrs :: Map Name DataType -> [ContextCtr]
buildContextCtrs inMap = concatMap buildContextLines . Map.elems $ inMap
where
buildContextLines :: DataType -> [ContextCtr]
buildContextLines (WrapsList dt cn ct) = [ListCC {
ctxCtrName = (childName ++ "To" ++ conName),
ctxCtrTypeFrom = ct,
ctxCtrTypeTo = dt,
ctxCtrCtrTo = cn
}]
where
childName = nameBase ct
conName = nameBase cn
buildContextLines (DataType dtName cons) = concatMap mkNormalCCs cons
where
mkNormalCCs :: Constructor -> [ContextCtr]
mkNormalCCs (Ctr cn kids) = catMaybes $ mapWithContext kids maybeMkNormalCC
where
conName = nameBase cn
maybeMkNormalCC :: [Child] -> Child -> [Child] -> Maybe ContextCtr
maybeMkNormalCC _ (NonNavigable _) _ = Nothing
maybeMkNormalCC before (Navigable n) after = Just $ NormalCC {
ctxCtrName = (childName ++ "To" ++ conName ++ count),
ctxCtrOffset_ = mcount,
ctxCtrTypesBefore = map childType before,
ctxCtrTypesAfter = map childType after,
ctxCtrTypeFrom = n,
ctxCtrTypeTo = dtName,
ctxCtrCtrTo = cn
}
where
childName = nameBase n
count = maybe "" show mcount
mcount
| null (sameSiblingsBefore ++ sameSiblingsAfter) = Nothing
| otherwise = Just . length $ sameSiblingsBefore
where
sameSiblingsBefore = filter ( (==n) . childType ) before
sameSiblingsAfter = filter ( (==n) . childType ) after
mapWithContext :: [a] -> ([a] -> a -> [a] -> b) -> [b]
mapWithContext xs f = mapWithContext' xs f []
where
mapWithContext' [] _ _ = []
mapWithContext' (x:xs) f r = (f r x xs):(mapWithContext' xs f (r ++ [x]))
extractConstructors :: Info -> [Con]
extractConstructors (TyConI dec) = getConstructors dec
extractConstructors _ = []
getConstructors :: Dec -> [Con]
getConstructors (DataD _ _ _ cons _) = cons
getConstructors (NewtypeD _ _ _ con _) = [con]
getConstructors _ = []
getInfoName :: Info -> Name
getInfoName (TyConI (DataD _ n _ _ _)) = n
getInfoName (TyConI (NewtypeD _ n _ _ _)) = n
getInfoName x = error $ "Don't know how to get name for: " ++ show x
getConName :: Con -> Name
getConName (NormalC n _) = n
getConName (RecC n _) = n
getConName x = error $ "GetConName: " ++ show x
extractChildTypesRaw :: Con -> [Type]
extractChildTypesRaw con = case con of
(RecC _ types) -> map thd $ types
(NormalC _ types) -> map snd $ types
_ -> []
extractTypeNoList :: Type -> Name
extractTypeNoList (ConT name) = name
extractTypeNoList (AppT (ConT lst) rhs)
| lst == ''[] = (extractTypeNoList rhs)
extractTypeNoList x = error $ show x
isList :: Type -> Bool
isList (AppT (ConT lst) _) = lst == ''[]
isList _ = False
thd :: (a,b,c) -> c
thd (_,_,c) = c
extractType :: Type -> Name
extractType (ConT name) = name
extractType (AppT (ConT lst) rhs)
| lst == ''[] = mkName $ "[" ++ nameBase (extractType rhs) ++ "]"
extractType x = error $ "Extract type; " ++ show x
extractChildTypes :: Con -> [Name]
extractChildTypes = catMaybes . map ect . extractChildTypesRaw
where
ect :: Type -> Maybe Name
ect (ConT name) = Just name
ect (AppT (ConT lst) rhs)
| lst == ''[] = ect rhs
ect _ = Nothing