packages feed

pms-domain-service-0.0.1.0: src/PMS/Domain/Service/DM/TH.hs

{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

module PMS.Domain.Service.DM.TH (
    funcTH_transit
  , instanceTH_IAppState
  ) where

import qualified GHC.Base
import qualified GHC.Show
import qualified Control.Monad.Fail
import Control.Monad.Trans.State.Lazy
import Language.Haskell.TH
import Control.Monad
import qualified Data.Text as T

import PMS.Domain.Service.DM.Type
import PMS.Domain.Service.DS.Utility


-- |
--
{-

> stack ghci --ghci-options=-XTemplateHaskell --ghci-options=-XQuasiQuotes
*Main> :m +Language.Haskell.TH

:{
 runQ [d|
   instance IAppState StartStateData where
     actionS s (EventW r@EntryEvent{})      = action s r
   |]
:}

[InstanceD Nothing [] (AppT (ConT Type.IAppState) (ConT Type.StartState))
  [FunD Type.doActivity
    [Clause [VarP s_8,ConP Type.EventW [AsP r_9 (RecP Type.InitEvent [])]] (NormalB (AppE (AppE (VarE Type.action) (VarE s_8)) (VarE r_9))) [],
     Clause [VarP s_10,ConP Type.EventW [AsP r_11 (RecP Type.TerminateEvent [])]] (NormalB (AppE (AppE (VarE Type.action) (VarE s_10)) (VarE r_11))) []
    ]
  ]
]

[InstanceD Nothing [] (AppT (ConT Type.IAppState) (ConT Type.StartStateData))
  [FunD Type.actionS 
    [Clause [VarP s_0,ConP Type.EventW [] [AsP r_1 (RecP Type.EntryEvent [])]] (NormalB (AppE (AppE (VarE Type.action) (VarE s_0)) (VarE r_1))) []
    ]
  ]
]


:{
 runQ [d|
   data Event r where
     InitEvent :: Event InitEvent
   |]
:}

[DataD [] Event_12 [PlainTV r_14] Nothing [GadtC [InitEvent_13] [] (AppT (ConT Event_12) (ConT Type.InitEvent))] []]

-}

instanceTH_IAppState :: Name -> Q [Dec]
instanceTH_IAppState stName = do
  ns <- getGadtsContNames ''Event
  clauseList <- mapM go ns
  return $ [InstanceD Nothing [] (AppT (ConT ''IAppState) (ConT stName)) [FunD 'actionS clauseList]]

  where
    -- |
    --
    go n = do
      s <- newName "s"
      r <- newName "r"
      return $ Clause [VarP s, ConP 'EventW [] [AsP r (RecP n [])]] (NormalB (AppE (AppE (VarE 'action) (VarE s)) (VarE r))) []

    -- |
    --
    getGadtsContNames :: Name -> Q [Name]
    getGadtsContNames n = reify n >>= \case
      TyConI (DataD _ _ _ _ cs _) -> mapM go' cs
      x -> fail $ "[ERROR] can not get data constructor. " ++ show x

      where
        go' (GadtC [name] _ _) = return name
        go' x = fail $ "[ERROR] can not get gadts data constructor. " ++ show x


-- |
--
{-

> stack ghci --ghci-options=-XTemplateHaskell --ghci-options=-XQuasiQuotes --ghci-options=-XLambdaCase
*Main> :m +Language.Haskell.TH

:{
 runQ [d|
    transit :: StateTransition -> AppContext ()
    transit StartToRun = get >>= \case
      AppStateW StartState -> changeTo $ AppStateW RunState
      AppStateW x -> fail $ "invalid state transition. trans:" ++ show StartToRun ++ ", curSt:" ++ show x
    transit RunToStop = get >>= \case
      AppStateW RunState -> changeTo $ AppStateW StopState
      AppStateW x -> fail $ "invalid state transition. trans:" ++ show StartToRun ++ ", curSt:" ++ show x
   |]
:}

[SigD transit_0 (AppT (AppT ArrowT (ConT Type.StateTransition)) (AppT (ConT Type.AppStateContext) (TupleT 0))),
  FunD transit_0 [
    Clause [ConP Type.StartToRun []]
     (NormalB (InfixE (Just (UnboundVarE get)) (VarE GHC.Base.>>=) (Just (LamCaseE [
        Match (ConP Type.AppStateW [ConP Type.StartState []]) (NormalB (InfixE (Just (UnboundVarE changeTo)) (VarE GHC.Base.$) (Just (AppE (ConE Type.AppStateW) (ConE Type.RunState))))) [],
        Match (ConP Type.AppStateW [VarP x_5])
          (NormalB (InfixE
            (Just (VarE GHC.Base.fail))
            (VarE GHC.Base.$)
            (Just (InfixE
              (Just (LitE (StringL "invalid state transition. trans:")))
                (VarE GHC.Base.++)
                (Just (InfixE
                  (Just (AppE (VarE GHC.Show.show) (ConE Type.StartToRun)))
                  (VarE GHC.Base.++)
                  (Just (InfixE
                    (Just (LitE (StringL ", curSt:")))
                      (VarE GHC.Base.++)
                      (Just (AppE (VarE GHC.Show.show) (VarE x_1)))
                  ))
                ))
            ))
          )) []
      ])))) [],
    Clause [
      ConP Type.RunToStop []] (NormalB (InfixE (Just (UnboundVarE get)) (VarE GHC.Base.>>=) (Just (LamCaseE [
        Match (ConP Type.AppStateW [ConP Type.RunState []]) (NormalB (InfixE (Just (UnboundVarE changeTo)) (VarE GHC.Base.$) (Just (AppE (ConE Type.AppStateW) (ConE Type.StopState))))) [],
        Match (VarP x_2) (NormalB (InfixE (Just (VarE GHC.Base.fail)) (VarE GHC.Base.$) (Just (InfixE (Just (LitE (StringL "invalid state transition. trans:"))) (VarE GHC.Base.++) (Just (InfixE (Just (AppE (VarE GHC.Show.show) (ConE Type.StartToRun))) (VarE GHC.Base.++) (Just (InfixE (Just (LitE (StringL ", curSt:"))) (VarE GHC.Base.++) (Just (AppE (VarE GHC.Show.show) (VarE x_2))))))))))) []])))) []]]
    ]
]

[SigD transit_2 (AppT (AppT ArrowT (ConT Type.StateTransition)) (AppT (ConT Type.AppContext) (TupleT 0))),
  FunD transit_2 [
    Clause [ConP Type.StartToRun [] []]
      (NormalB (InfixE (Just (VarE Control.Monad.Trans.State.Lazy.get)) (VarE GHC.Internal.Base.>>=) (Just (LamCaseE [
        Match (ConP Type.AppStateW [] [ConP Type.StartState [] []]) (NormalB (InfixE (Just (UnboundVarE changeTo)) (VarE GHC.Internal.Base.$) (Just (AppE (ConE Type.AppStateW) (ConE Type.RunState))))) [],
        Match (ConP Type.AppStateW [] [VarP x_3])
          (NormalB (InfixE 
            (Just (VarE GHC.Internal.Control.Monad.Fail.fail))
              (VarE GHC.Internal.Base.$) 
              (Just (InfixE
                (Just (LitE (StringL "invalid state transition. trans:")))
                  (VarE GHC.Internal.Base.++)
                  (Just (InfixE 
                    (Just (AppE (VarE GHC.Internal.Show.show) (ConE Type.StartToRun))) 
                    (VarE GHC.Internal.Base.++)
                    (Just (InfixE (Just (LitE (StringL ", curSt:")))
                      (VarE GHC.Internal.Base.++)
                      (Just (AppE (VarE GHC.Internal.Show.show) (VarE x_3)))
                    ))
                  ))
                ))
              )) []
        ])))) [],
    Clause [ConP Type.RunToStop [] []] (NormalB (InfixE (Just (VarE Control.Monad.Trans.State.Lazy.get)) (VarE GHC.Internal.Base.>>=) (Just (LamCaseE [
      Match (ConP Type.AppStateW [] [ConP Type.RunState [] []]) (NormalB (InfixE (Just (UnboundVarE changeTo)) (VarE GHC.Internal.Base.$) (Just (AppE (ConE Type.AppStateW) (ConE Type.StopState))))) [],
      Match (ConP Type.AppStateW [] [VarP x_4]) (NormalB (InfixE (Just (VarE GHC.Internal.Control.Monad.Fail.fail)) (VarE GHC.Internal.Base.$) (Just (InfixE (Just (LitE (StringL "invalid state transition. trans:"))) (VarE GHC.Internal.Base.++) (Just (InfixE (Just (AppE (VarE GHC.Internal.Show.show) (ConE Type.StartToRun))) (VarE GHC.Internal.Base.++) (Just (InfixE (Just (LitE (StringL ", curSt:"))) (VarE GHC.Internal.Base.++) (Just (AppE (VarE GHC.Internal.Show.show) (VarE x_4))))))))))) []])))) []
    ]
]


-}

funcTH_transit :: Q [Dec]
funcTH_transit = do
  fname <- newName "transit"
  cons <- getContNames ''StateTransition
  clauses <- mapM makeClaues cons

  return $ [SigD fname (AppT (AppT ArrowT (ConT ''StateTransition)) (AppT (ConT ''AppStateContext) (TupleT 0))),
            FunD fname clauses
           ]

  where
    -- |
    --
    getContNames :: Name -> Q [Name]
    getContNames n = reify n >>= \case
      TyConI (DataD _ _ _ _ cs _) -> mapM go cs
      x -> fail $ "[ERROR] can not get data constructor. " ++ show x

      where
        go (NormalC x _) = return x
        go x = fail $ "[ERROR] can not get data constructor. " ++ show x

    -- |
    --
    makeClaues :: Name -> Q Clause
    makeClaues n = do
      x <- newName "x"
      (curSt, nexSt) <- getStName n
      return $ Clause
        [ConP n [] []]
        (NormalB
          (InfixE (Just (UnboundVarE 'get)) (VarE '(GHC.Base.>>=))
            (Just (LamCaseE [
              Match (ConP 'AppStateW [] [ConP curSt [] []])
                (NormalB (InfixE (Just (UnboundVarE 'changeTo)) (VarE '(GHC.Base.$)) (Just (AppE (ConE 'AppStateW) (ConE nexSt))) )) [],
              Match (ConP 'AppStateW [] [VarP x])
                (NormalB (InfixE
                  (Just (VarE 'Control.Monad.Fail.fail))
                  (VarE '(GHC.Base.$))
                  (Just (InfixE
                    (Just (LitE (StringL "invalid state transition. trans:")))
                      (VarE '(GHC.Base.++))
                      (Just (InfixE
                        (Just (AppE (VarE 'GHC.Show.show) (ConE n)))
                        (VarE '(GHC.Base.++))
                        (Just (InfixE
                          (Just (LitE (StringL ", curSt:")))
                            (VarE '(GHC.Base.++))
                            (Just (AppE (VarE 'GHC.Show.show) (VarE x)))
                        ))
                      ))
                  ))
                )) []
            ]))
          )
        )
        []

    -- |
    --
    getStName :: Name -> Q (Name, Name)
    getStName n = do
      let modName = "PMS.Domain.Service.DM.Type."

      let stStrs = T.splitOn "To" $ T.replace modName "" $ T.pack $ show n
      when (2 /= length stStrs) $ fail $ "[ERROR] invalid StateTransition constructor. " ++ show n

      let curSt = mkName $ T.unpack $ modName `T.append` head stStrs `T.append` "State"
          nxtSt = mkName $ T.unpack $ modName `T.append` last stStrs `T.append` "State"

      return (curSt, nxtSt)