packages feed

storable-endian-0.1.0: Data/Storable/EndianTemplate.hs

module Data.Storable.EndianTemplate where

import Data.Char

import Unsafe.Coerce
import Foreign.Ptr

import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import Control.Monad

concatM :: (Monad m) => [m [a]] -> m [a]
concatM [] = return []
concatM (m:ms) = liftM2 (++) m (concatM ms)

deriveEndian :: String -> Int -> Int -> Q [Dec]
deriveEndian baseName bytes bits = concatM [mapM declareNewtype ["le", "be"], mapM deriveEndian' ["le", "be"]]
  where
    declareNewtype end = do
      baseType <- conT (mkName baseName)
      let typeName = baseName ++ map toUpper end
      newtypeD (cxt []) (mkName typeName) [] (return $ NormalC (mkName typeName) [(NotStrict, baseType)]) []
    deriveEndian' end = do
      let typeName = mkName $ baseName ++ map toUpper end
      let getter   = mkName $ "getWord" ++ show bits ++ end
      let putter   = mkName $ "putWord" ++ show bits ++ end
      let vp = mkName "p"
      let vx = mkName "x"
      pokeBody <- [| $(varE putter) (unsafeCoerce $(varE vx)) (castPtr $(varE vp))|]
      members <- concatM [
          [d| sizeOf _ = $(lift bytes) |]
         ,[d| alignment _ = $(lift bytes) |]
         ,[d| peek p = ($(conE typeName) . unsafeCoerce) `fmap` $(varE getter) (castPtr p) |]
         ,return [FunD (mkName "poke") [Clause 
            [VarP (mkName "p"), ConP typeName [VarP (mkName "x")]] 
            (NormalB pokeBody)
            []]]
        ]
      instanceD (cxt []) (appT (conT (mkName "Storable")) (conT typeName)) (map return members)