aboutsummaryrefslogtreecommitdiff
path: root/hsm-core/Hsm/Core/Serial.hs
blob: 0fc89e8e61ffc6c38f6df89e8ecb686c2c2cf74d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{-# 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) []