{-# LANGUAGE TemplateHaskellQuotes #-} module Hsm.Core.Serial ( makeSerial ) where import GHC.Num (integerFromInt) import Language.Haskell.TH ( Body(NormalB) , Clause(Clause) , Con(NormalC) , Dec(DataD, FunD, SigD) , DerivClause(DerivClause) , Exp(LitE) , Lit(IntegerL) , Name , Pat(ConP) , Q , Type(AppT, ArrowT, ConT) , mkName ) -- Generates a serial data type with the given name and a set of constructors, -- each mapped to a corresponding integer value. -- -- - The data type derives `Bounded`, `Enum`, and `Show` for convenience. -- - A companion mapping function is also generated, converting each constructor -- to its associated integer. -- -- For debugging purposes, use `-ddump-splices` to inspect the generated code. -- -- Example: -- -- $(makeSerial "GPIO" "Pin" "pinLine" ''Int [2, 3, 4]) -- -- Generates a data type `GPIOPin` with constructors `GPIO2`, `GPIO3` `GPIO4`, -- and a function `pinLine :: GPIOPin -> Int`. makeSerial :: String -> String -> String -> Name -> [Int] -> Q [Dec] makeSerial name suffix mapFun mapType idxs = return [DataD [] dtName [] Nothing (idxCons <$> idxs) [derivClause], SigD mapFunName . AppT (AppT ArrowT $ ConT dtName) $ ConT mapType, FunD mapFunName $ mapFunClause <$> idxs] where dtName = mkName $ name <> suffix idxName idx = mkName $ name <> show idx idxCons idx = NormalC (idxName idx) [] derivClause = DerivClause Nothing [ConT ''Bounded, ConT ''Enum, ConT ''Show] mapFunName = mkName mapFun mapFunBody = NormalB . LitE . IntegerL . integerFromInt mapFunClause idx = Clause [ConP (idxName idx) [] []] (mapFunBody idx) []