module Type.HS2PS where import Language.Haskell.TH import Control.Monad import Control.Monad.Fail (MonadFail) import Data.List import Data.Traversable import Data.Char import Control.Arrow import Data.Functor renderPSTypes :: [Name] -> ExpQ renderPSTypes = litE . stringL . intercalate "\n" <=< traverse def2PS def2PS :: Name -> Q String def2PS = reify >=> decPS >=> tyConPS type2PS :: Name -> ExpQ type2PS = litE . stringL . typeMap decPS :: Info -> DecQ decPS = \case ClassI _ _ -> unacceptedConstructor "Info" "ClassI" ClassOpI _ _ _ -> unacceptedConstructor "Info" "ClassOpI" FamilyI _ _ -> unacceptedConstructor "Info" "FamilyI" PrimTyConI _ _ _ -> unacceptedConstructor "Info" "PrimTyConI" DataConI _ _ _ -> unacceptedConstructor "Info" "DataConI" PatSynI _ _ -> unacceptedConstructor "Info" "PatSynI" VarI _ _ _ -> unacceptedConstructor "Info" "VarI" TyVarI _ _ -> unacceptedConstructor "Info" "TyVarI" TyConI tyConIDec -> pure tyConIDec tyConPS :: Dec -> Q String tyConPS = \case FunD _ _ -> unacceptedConstructor "TyConI" "FunD" ValD _ _ _ -> unacceptedConstructor "TyConI" "ValD" ClassD _ _ _ _ _ -> unacceptedConstructor "TyConI" "ClassD" InstanceD _ _ _ _ -> unacceptedConstructor "TyConI" "InstanceD" SigD _ _ -> unacceptedConstructor "TyConI" "SigD" ForeignD _ -> unacceptedConstructor "TyConI" "ForeignD" InfixD _ _ -> unacceptedConstructor "TyConI" "InfixD" PragmaD _ -> unacceptedConstructor "TyConI" "PragmaD" DataFamilyD _ _ _ -> unacceptedConstructor "TyConI" "DataFamilyD" DataInstD _ _ _ _ _ _ -> unacceptedConstructor "TyConI" "DataInstD" NewtypeInstD _ _ _ _ _ _ -> unacceptedConstructor "TyConI" "NewtypeInstD" TySynInstD _ _ -> unacceptedConstructor "TyConI" "TySynInstD" OpenTypeFamilyD _ -> unacceptedConstructor "TyConI" "OpenTypeFamilyD" ClosedTypeFamilyD _ _ -> unacceptedConstructor "TyConI" "ClosedTypeFamilyD" RoleAnnotD _ _ -> unacceptedConstructor "TyConI" "RoleAnnotD" StandaloneDerivD _ _ _ -> unacceptedConstructor "TyConI" "StandaloneDerivD" DefaultSigD _ _ -> unacceptedConstructor "TyConI" "DefaultSigD" PatSynD _ _ _ _ -> unacceptedConstructor "TyConI" "PatSynD" PatSynSigD _ _ -> unacceptedConstructor "TyConI" "PatSynSigD" TySynD typeName typeVars t -> renderTypeAlias typeName typeVars t NewtypeD [] typeName typeVars Nothing constructor _ -> renderNewtype typeName typeVars constructor DataD [] typeName typeVars Nothing [constructor] _ -> case constructor of NormalC _ (_:_:_) -> renderData typeName typeVars [constructor] _ -> renderNewtype typeName typeVars constructor DataD [] typeName typeVars Nothing constructors _ -> renderData typeName typeVars constructors x -> fail $ "tyConPS does not support: " <> show x unacceptedConstructor :: MonadFail m => String -> String -> m a unacceptedConstructor typeName constructorName = fail $ "mkDef2PS does not accept " <> typeName <> " constructor: " <> constructorName renderTypeAlias :: Name -> [TyVarBndr] -> Type -> Q String renderTypeAlias typeName typeVars t = do tvs <- case typeVars of [] -> pure "" _ -> (' ' :) . intercalate " " <$> for typeVars \case PlainTV tv -> pure $ nameBase tv x -> fail $ "renderTypeAlias cannot render: " <> show x rt <- renderType t pure $ "type " <> nameBase typeName <> " " <> tvs <> "= " <> rt renderData :: Name -> [TyVarBndr] -> [Con] -> Q String renderData typeName typeVars constructors = do cs <- intercalate " | " <$> traverse renderConstructor constructors rTVars <- traverse renderTypeVariables typeVars <&> \vs -> if null vs then "" else ' ' : intercalate " " vs pure $ "data " <> nameBase typeName <> rTVars <> " = " <> cs renderNewtype :: Name -> [TyVarBndr] -> Con -> Q String renderNewtype typeName typeVars constructor = do rCon <- renderConstructor constructor rTVars <- traverse renderTypeVariables typeVars <&> \vs -> if null vs then "" else ' ' : intercalate " " vs pure $ "newtype " <> nameBase typeName <> rTVars <> " = " <> rCon renderConstructor :: Con -> Q String renderConstructor = \case NormalC conName types -> do renderedTypes <- for types \(_,t) -> opParen <$> renderType t pure $ nameBase conName <> (if null renderedTypes then "" else " ") <> intercalate " " renderedTypes RecC conName types -> do renderedTypes <- for types \(accessorName,_,conType) -> (\rt -> nameBase accessorName <> " :: " <> opParen rt) <$> renderType conType pure $ nameBase conName <> " {" <> intercalate ", " renderedTypes <> "}" x -> fail $ "renderConstructor is does not support: " <> show x renderTypeVariables :: TyVarBndr -> Q String renderTypeVariables = \case PlainTV n -> pure $ nameBase n KindedTV n StarT -> pure $ nameBase n KindedTV n kind -> fail $ "renderTypeVariables does not accept: KindedTV " <> nameBase n <> " " <> show kind renderType :: Type -> Q String renderType = \case ConT n -> pure $ typeMap n AppT x y -> do x' <- renderType x y' <- renderType y pure $ x' <> " " <> opParen y' TupleT 2 -> pure "Tuple" VarT n -> pure $ nameBase n ListT -> pure "Array" x -> fail $ "Could not renderType: " <> show x typeMap :: Name -> String typeMap = nameBase >>> \case "Word" -> "Int" "Double" -> "Number" "()" -> "Unit" "Text" -> "String" "ByteString" -> "String" x -> x opParen :: String -> String opParen xs | any isSpace xs = "(" <> xs <> ")" | otherwise = xs