{-|
Copyright  :  (C) 2018, Google Inc.
License    :  BSD2 (see the file LICENSE)
Maintainer :  Christiaan Baaij <christiaan.baaij@gmail.com>

Using /ANN/ pragma's you can tell the Clash compiler to use a custom
bit representation for a data type. See @DataReprAnn@ for documentation.

-}

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}

{-# OPTIONS_GHC -Wno-orphans #-}

module Clash.Annotations.BitRepresentation
 (
 -- * Data structures to express a custom bit representation
   DataReprAnn(..)
 , ConstrRepr(..)
 -- * Convenience type synonyms for Integer
 , BitMask
 , Value
 , Size
 , FieldAnn

 -- * Functions
 , liftQ
 ) where

import           Data.Data                  (Data)
import           Data.Typeable              (Typeable)
import           Language.Haskell.TH.Instances ()
import qualified Language.Haskell.TH.Lift   ()
import qualified Language.Haskell.TH.Syntax as TH
import           GHC.Generics               (Generic)

type BitMask  = Integer
type Value    = Integer
type Size     = Int

-- | BitMask used to mask fields
type FieldAnn = BitMask

-- | Lift values inside of 'TH.Q' to a Template Haskell expression
liftQ :: TH.Lift a => TH.Q a -> TH.Q TH.Exp
liftQ = (>>= TH.lift)

-- NOTE: The following instances are imported from Language.Haskell.TH.Lift.
-- This module also implements 'instance Lift Exp', which might make debugging
-- template haskell more difficult. Please uncomment these instances and the
-- import of TH.Lift whenever it suits you.
--
--deriving instance TH.Lift TH.Name
--deriving instance TH.Lift TH.OccName
--deriving instance TH.Lift TH.NameFlavour
--deriving instance TH.Lift TH.ModName
--deriving instance TH.Lift TH.NameSpace
--deriving instance TH.Lift TH.PkgName


-- | Annotation for custom bit representations of data types
--
-- Using /ANN/ pragma's you can tell the Clash compiler to use a custom
-- bit-representation for a data type.
--
-- For example:
--
-- @
-- data Color = R | G | B
-- {-# ANN module (DataReprAnn
--                   $(liftQ [t|Color|])
--                   2
--                   [ ConstrRepr 'R 0b11 0b00 []
--                   , ConstrRepr 'G 0b11 0b01 []
--                   , ConstrRepr 'B 0b11 0b10 []
--                   ]) #-}
-- @
--
-- This specifies that @R@ should be encoded as 0b00, @G@ as 0b01, and
-- @B@ as 0b10. The first binary value in every @ConstrRepr@ in this example
-- is a mask, indicating which bits in the data type are relevant. In this case
-- all of the bits are.
--
-- Or if we want to annotate @Maybe Color@:
--
-- @
-- {-# ANN module ( DataReprAnn
--                    $(liftQ [t|Maybe Color|])
--                    2
--                    [ ConstrRepr 'Nothing 0b11 0b11 []
--                    , ConstrRepr 'Just 0b00 0b00 [0b11]
--                    ] ) #-}
-- @
--
-- By default, @Maybe Color@ is a data type which consumes 3 bits. A single bit
-- to indicate the constructor (either @Just@ or @Nothing@), and two bits to encode
-- the first field of @Just@. Notice that we saved a single bit by exploiting
-- the fact that @Color@ only uses three values (0, 1, 2), but takes two bits
-- to encode it. We can therefore use the last - unused - value (3), to encode
-- one of the constructors of @Maybe@. We indicate which bits encode the
-- underlying @Color@ field of @Just@ by passing /[0b11]/ to ConstrRepr. This
-- indicates that the first field is encoded in the first and second bit of the
-- whole datatype (0b11).
--
-- __NB__: BitPack for a custom encoding can be derived using
-- 'Clash.Annotations.BitRepresentation.Deriving.deriveBitPack'.
data DataReprAnn =
  DataReprAnn
    -- Type this annotation is for:
    TH.Type
    -- Size of type:
    Size
    -- Constructors:
    [ConstrRepr]
      deriving (Show, Data, Typeable, Eq, Generic, TH.Lift)

-- | Annotation for constructors. Indicates how to match this constructor based
-- off of the whole datatype.
data ConstrRepr =
  ConstrRepr
    -- Constructor name:
    TH.Name
    -- Bits relevant for this constructor:
    BitMask
    -- data & mask should be equal to..:
    Value
    -- Masks for fields. Indicates where fields are stored:
    [FieldAnn]
      deriving (Show, Data, Typeable, Eq, Generic, TH.Lift)