packages feed

GLFW-b-1.0.0: Graphics/UI/GLFW/Internal/C/TH.hs

{-# LANGUAGE TemplateHaskell #-}

-- deriveC creates an instance of C c h from [(c, h)].
--
-- Example:
--
-- > pairs :: [(CInt, Bool)]
-- > pairs = [(0, False), (1, True)]
-- >
-- > deriveC ''CInt ''Bool pairs
--
-- would derive
--
-- > instance C CInt Bool where
-- >   fromC 0 = False
-- >   fromC 1 = True
-- >   fromC _ = error ...
-- >
-- >   toC False = 0
-- >   toC True  = 1

module Graphics.UI.GLFW.Internal.C.TH
  ( deriveC
  ) where

--------------------------------------------------------------------------------

import Data.Data                  (Data)
import Data.Tuple                 (swap)
import Language.Haskell.TH        (Dec, Name, Q, appT, clause, conT, cxt, funD, global, instanceD, mkName, nameBase, normalB, varP)
import Language.Haskell.TH.Quote  (dataToPatQ)
import Language.Haskell.TH.Syntax (Lift(..))

--------------------------------------------------------------------------------

deriveC :: (Data c, Data h, Lift c, Lift h) => Name -> Name -> [(c, h)] -> Q [Dec]
deriveC ctype htype pairs =
    (:[]) `fmap` deriveC1 ctype htype pairs

--------------------------------------------------------------------------------

deriveC1 :: (Data c, Data h, Lift c, Lift h) => Name -> Name -> [(c, h)] -> Q Dec
deriveC1 _ _ [] =
    error "deriveC: no pairs!"
deriveC1 ctype htype pairs =
    instanceD
      (cxt [])
      (appT
          (appT (conT cname) (conT ctype))
          (conT htype)
      )
      [ funD frname frClauses
      , funD toname toClauses
      ]
  where
    cname  = mkName "C"
    frname = mkName "fromC"
    toname = mkName "toC"
    vname  = mkName "v"

    frClauses = map  genPairClause         pairs ++ [genErrorClause]
    toClauses = map (genPairClause . swap) pairs

    genPairClause (v0, v1) =
        clause [pat] body []
      where
        pat  = dataToPatQ (const Nothing) v0
        body = normalB [|v1|]

    genErrorClause =
        clause [pat] body []
      where
        pat   = varP vname
        body  = normalB [| error $ msg ++ show $(global vname) |]
        -- e.g., "C CInt Bool fromC: "
        msg = unwords (map nameBase [cname, ctype, htype, frname]) ++ ": "