{-# LANGUAGE TemplateHaskellQuotes #-}
module Clash.Core.TermLiteral.TH
( deriveTermToData
) where
import Data.Either
import qualified Data.Text as Text
import Language.Haskell.TH.Syntax
import Clash.Core.DataCon
import Clash.Core.Term (collectArgs, Term(Data))
import Clash.Core.Name (nameOcc)
import Clash.Core.Subst ()
dcName' :: DataCon -> String
dcName' = Text.unpack . nameOcc . dcName
termToDataName :: Name
termToDataName = mkName "Clash.Core.TermLiteral.termToData"
deriveTermToData :: Name -> Q Exp
deriveTermToData typName = do
TyConI (DataD _ _ _ _ constrs _) <- reify typName
pure (deriveTermToData1 (map toConstr' constrs))
where
toConstr' (NormalC cName fields) = (cName, length fields)
toConstr' c = error $ "Unexpected constructor: " ++ show c
deriveTermToData1 :: [(Name, Int)] -> Exp
deriveTermToData1 constrs =
LamE
[pat]
(if null args then theCase else LetE args theCase)
where
nArgs = maximum (map snd constrs)
args :: [Dec]
args = zipWith (\n nm -> ValD (VarP nm) (NormalB (arg n)) []) [0..] argNames
arg n = UInfixE (VarE argsName) (VarE '(!!)) (LitE (IntegerL n))
theCase :: Exp
theCase =
CaseE
(VarE nameName)
(map match constrs ++ [emptyMatch])
emptyMatch = Match WildP (NormalB (ConE 'Left `AppE` VarE termName)) []
match :: (Name, Int) -> Match
match (cName, nFields) =
Match (LitP (StringL (show cName))) (NormalB (mkCall cName nFields)) []
mkCall :: Name -> Int -> Exp
mkCall cName 0 = ConE 'Right `AppE` ConE cName
mkCall cName 1 =
UInfixE
(ConE cName)
(VarE '(<$>))
(VarE termToDataName `AppE` VarE (head argNames))
mkCall cName nFields =
foldl
(\e aName ->
UInfixE
e
(VarE '(<*>))
(VarE termToDataName `AppE` VarE aName))
(mkCall cName 1)
(take (nFields-1) (tail argNames))
pat :: Pat
pat =
AsP
termName
(ViewP
(VarE 'collectArgs)
(TupP [ ConP 'Data [ViewP (VarE 'dcName') (VarP nameName)]
, ViewP
(VarE 'lefts)
(if nArgs == 0 then WildP else VarP argsName)]))
termName = mkName "term"
argsName = mkName "args"
argNames = [mkName ("arg" ++ show n) | n <- [0..nArgs-1]]
nameName = mkName "nm"