{-# LANGUAGE UndecidableInstances #-}
module Data.Type.BitRecords.Structure where
import Data.Int
import Data.Kind ( Type )
import Data.Kind.Extra
import Data.Proxy
import Data.Type.Pretty
import Data.Word
import GHC.TypeLits
import Data.Type.Bool
import Data.Type.Equality (type (==))
import Test.TypeSpec
import qualified Data.Type.BitRecords.Structure.TypeLits as Literal
data Structure (sizeType :: StructureSizeType) where
MkVariableStructure :: Structure 'VarSize
MkFixStructure :: Structure 'FixSize
type FixStructure = Structure 'FixSize
type VariableStructure = Structure 'VarSize
data StructureSizeType = VarSize | FixSize
type family GetStructureSize (t :: Extends (Structure 'FixSize)) :: Nat
type family PrettyStructure (struct :: Extends (Structure sizeType)) :: PrettyType
type instance ToPretty (struct :: Extends (Structure sizeType)) = PrettyStructure struct
data EmptyStructure :: Extends (Structure 'FixSize)
type instance GetStructureSize EmptyStructure = 0
type instance PrettyStructure EmptyStructure = 'PrettyEmpty
type instance GetStructureSize (Anonymous (Name name struct)) = GetStructureSize struct
type instance PrettyStructure (Anonymous (Name name struct)) = name <:> PrettyStructure struct
data Record :: [Extends (Named (Structure sizeType))] -> Extends (Structure sizeType)
type instance GetStructureSize (Record '[]) = 0
type instance GetStructureSize (Record (x ': xs)) = GetStructureSize (Anonymous x) + GetStructureSize (Record xs)
type instance PrettyStructure (Record xs) = "Record" <:$$--> PrettyRecord xs
type family (<>) (a :: Extends (Named (Structure sizeType))) (b :: k) :: Extends (Structure sizeType) where
a <> (b :: Extends (Named (Structure sizeType))) = Record '[a, b]
a <> (Record xs) = Record (a ': xs)
infixr 6 <>
type family PrettyRecord (xs :: [Extends (Named (Structure sizeType))]) :: PrettyType where
PrettyRecord '[] = 'PrettyEmpty
PrettyRecord (x ': xs) =
(PutStr "-" <+> PrettyStructure (Anonymous x)) <$$> PrettyRecord xs
data BitSequence (length :: Nat) :: Extends (Structure 'FixSize)
type family WithValidBitSequenceLength (length :: Nat) (out :: k) :: k where
WithValidBitSequenceLength length out =
If (length <=? 64 && 1 <=? length )
out
(TypeError ('Text "invalid bit sequence length: " ':<>: 'ShowType length))
type family (//) (name :: Symbol) (length :: Nat) :: Extends (Named (Structure 'FixSize)) where
name // length =
WithValidBitSequenceLength length (Name name (BitSequence length))
infixr 7 //
type instance GetStructureSize (BitSequence length) =
WithValidBitSequenceLength length length
type instance PrettyStructure (BitSequence length) =
WithValidBitSequenceLength length (PutStr "BitSequence " <+> PutNat length)
data LiteralStructure a :: Extends (Structure 'FixSize)
type instance GetStructureSize (LiteralStructure (Literal.Value s k (x :: k))) = Literal.SizeOf s x
type instance PrettyStructure (LiteralStructure (Literal.Value s k (x :: k))) = "LiteralStructure" <:> Literal.Pretty s x
data Padded (literal :: Either j j) (struct :: Extends (Structure 'FixSize)) :: Extends (Structure 'FixSize)
type family GetPaddingSize literal (struct :: Extends (Structure 'FixSize)) :: Nat where
GetPaddingSize (Literal.Value s k (x :: k)) y =
((Literal.SizeOf s x) - ((GetStructureSize y) `Mod` (Literal.SizeOf s x))) `Mod` (Literal.SizeOf s x)
type family PaddedStructureValidateSize literal (rhs :: Extends (Structure 'FixSize)) (out :: k) :: k where
PaddedStructureValidateSize (Literal.Value s k (x :: k)) rhs out =
If (Literal.SizeOf s x == 0)
(TypeError ('Text "Cannot use a literal of size 0: "
':<>: 'ShowType (Literal.Value s k (x :: k))
':<>: 'Text " to pad the structure: "
':<>: 'ShowType rhs))
out
type instance GetStructureSize (Padded (either literal) struct) =
PaddedStructureValidateSize literal struct (GetPaddingSize literal struct + GetStructureSize struct)
type instance PrettyStructure (Padded (either literal) struct) =
PrettyParens (PrettyStructure struct) <+> ("padded with" <:> PutNat (GetPaddingSize literal struct) <+> PutStr "bits.")
data Alias :: Extends (Structure 'FixSize) -> Extends (Structure 'FixSize) -> Extends (Structure 'FixSize)
type family AliasStructureValidateSize (lhs :: Extends (Structure 'FixSize)) (rhs :: Extends (Structure 'FixSize)) (out :: k) :: k where
AliasStructureValidateSize lhs rhs out =
If (GetStructureSize rhs == GetStructureSize lhs)
out
(TypeError ('Text "Cannot alias structures of different size " ':<>: 'ShowType rhs
':<>: 'Text " requires " ':<>: 'ShowType (GetStructureSize rhs)
':<>: 'Text " bits, but " ':<>: 'ShowType lhs
':<>: 'Text " requires " ':<>: 'ShowType (GetStructureSize lhs)
':<>: 'Text " bits."))
type instance GetStructureSize (Alias lhs rhs) = AliasStructureValidateSize lhs rhs (GetStructureSize lhs)
type instance PrettyStructure (Alias lhs rhs) =
AliasStructureValidateSize lhs rhs
("Alias" <:$$--> (PrettyStructure lhs <$$> PrettyStructure rhs))
data TypeStructure :: Type -> Extends (Structure 'FixSize)
type U8 = TypeStructure Word8
type instance GetStructureSize (TypeStructure Word8) = 8
type instance PrettyStructure (TypeStructure Word8) = ToPretty Word8
type S8 = TypeStructure Int8
type instance GetStructureSize (TypeStructure Int8) = 8
type instance PrettyStructure (TypeStructure Int8) = ToPretty Int8
type FlagStructure = TypeStructure Bool
type instance GetStructureSize (TypeStructure Bool) = 1
type instance PrettyStructure (TypeStructure Bool) = ToPretty Bool
data IntegerStructure :: Nat -> Sign -> Endianess -> Extends (Structure 'FixSize) where
S16LE :: Int16 -> IntegerStructure 16 'Signed 'LE 'MkFixStructure
S16BE :: Int16 -> IntegerStructure 16 'Signed 'BE 'MkFixStructure
U16LE :: Word16 -> IntegerStructure 16 'Unsigned 'LE 'MkFixStructure
U16BE :: Word16 -> IntegerStructure 16 'Unsigned 'BE 'MkFixStructure
S32LE :: Int32 -> IntegerStructure 32 'Signed 'LE 'MkFixStructure
S32BE :: Int32 -> IntegerStructure 32 'Signed 'BE 'MkFixStructure
U32LE :: Word32 -> IntegerStructure 32 'Unsigned 'LE 'MkFixStructure
U32BE :: Word32 -> IntegerStructure 32 'Unsigned 'BE 'MkFixStructure
S64LE :: Int64 -> IntegerStructure 64 'Signed 'LE 'MkFixStructure
S64BE :: Int64 -> IntegerStructure 64 'Signed 'BE 'MkFixStructure
U64LE :: Word64 -> IntegerStructure 64 'Unsigned 'LE 'MkFixStructure
U64BE :: Word64 -> IntegerStructure 64 'Unsigned 'BE 'MkFixStructure
data Endianess = LE | BE
data Sign = Signed | Unsigned
type family IntegerStructureValidateLength (n :: Nat) (out :: k) where
IntegerStructureValidateLength n out =
If (n == 16) out (If (n == 32) out (If (n == 64) out
(TypeError ('Text "Invalid IntegerStructure size: " ':<>: 'ShowType n))))
type instance GetStructureSize (IntegerStructure n s e) = IntegerStructureValidateLength n n
type instance PrettyStructure (IntegerStructure n s e) =
IntegerStructureValidateLength n
(PutStr "IntegerStructure"
<+> PutNat n
<+> If (s == 'Signed) (PutStr "Signed") (PutStr "Unsigned")
<+> If (e == 'BE) (PutStr "BE") (PutStr "LE"))
type U n e = IntegerStructure n 'Unsigned e
type S n e = IntegerStructure n 'Signed e
data ConditionalStructure (condition :: Bool) (ifStruct :: Extends (Structure 'FixSize)) (elseStruct :: Extends (Structure 'FixSize)) :: Extends (Structure 'FixSize)
type instance GetStructureSize (ConditionalStructure 'True l r) = GetStructureSize l
type instance GetStructureSize (ConditionalStructure 'False l r) = GetStructureSize r
type instance PrettyStructure (ConditionalStructure 'True l r) = PrettyStructure l
type instance PrettyStructure (ConditionalStructure 'False l r) = PrettyStructure r
showStructure
:: forall proxy (struct :: Extends (Structure 'FixSize))
. PrettyTypeShow (PrettyStructure struct)
=> proxy struct
-> String
showStructure _ = showPretty (Proxy :: Proxy (PrettyStructure struct))
data BoolProxy (t :: Bool) where
TrueProxy :: BoolProxy 'True
FalseProxy :: BoolProxy 'False
_typeSpecGetStructureSize
:: BoolProxy (testBool :: Bool)
-> Expect [ GetStructureSize U8 `ShouldBe` 8
, GetStructureSize EmptyStructure `ShouldBe` 0
, GetStructureSize (Record [Name "x" U8, Name "y" U8]) `ShouldBe` 16
, GetStructureSize (S 16 'BE) `ShouldBe` 16
, GetStructureSize (ConditionalStructure testBool (U 32 'LE) S8) `ShouldBe` (If testBool 32 8)
, GetStructureSize ("field 1"//3 <> "field 2"//2 <> "field 3"//5 <>
Name "field 4" ("field 4.1"//3 <> "field 4.2"//6))
`ShouldBe` 19
, GetStructureSize (Alias (BitSequence 4) (LiteralStructure (Literal.Bits '[1,0,0,1]))) `ShouldBe` 4
, GetStructureSize (Padded ('Left (Literal.Bits '[1,0,1,0,1,0,1,0])) ("x"//1 <> "y"//2)) `ShouldBe` 8
, GetStructureSize (Padded ('Right (Literal.Bits '[1,0,1,0,1,0,1,0])) (Anonymous ("x"//9))) `ShouldBe` 16
, GetStructureSize (Padded ('Left (Literal.Bits '[1,0,1,0,1,0,1,0])) (Anonymous ("x"//22))) `ShouldBe` 24
, GetStructureSize (Padded ('Right (Literal.Bits '[1,0,1,0,1,0,1,0])) (Anonymous ("x"//33))) `ShouldBe` 40
, GetStructureSize (Padded ('Right (Literal.To (Literal.Sequence Word8 Nat) '[0,255,0,255])) (Anonymous ("x"//10))) `ShouldBe` 32
]
_typeSpecGetStructureSize TrueProxy = Valid
_typeSpecGetStructureSize FalseProxy = Valid
_prettySpec :: String
_prettySpec =
showPretty (Proxy @(
PrettyHigh '[
PrettyStructure EmptyStructure
, PrettyStructure (Anonymous (Name "foo" U8))
, PrettyStructure U8
, PrettyStructure S8
, PrettyStructure FlagStructure
, PrettyStructure (S 16 'LE)
, PrettyStructure (S 32 'LE)
, PrettyStructure (S 64 'LE)
, PrettyStructure (U 16 'LE)
, PrettyStructure (U 32 'LE)
, PrettyStructure (U 64 'LE)
, PrettyStructure (S 16 'BE)
, PrettyStructure (S 32 'BE)
, PrettyStructure (S 64 'BE)
, PrettyStructure (U 16 'BE)
, PrettyStructure (U 32 'BE)
, PrettyStructure (Padded ('Left (Literal.Bits '[1,0,1,0,1,0,1,0])) ("x"//2 <> "y"//7 <> "z"//5))
, PrettyStructure (U 64 'BE)
, PrettyStructure ("x"//32 <> "y"//32 <> "z"//8)
, PrettyStructure (ConditionalStructure 'False S8 U8)
, PrettyStructure (ConditionalStructure 'True S8 U8)
, PrettyStructure (Alias S8 (Padded ('Left (Literal.To Word8 0)) (LiteralStructure (Literal.To Nat 123))))
, PrettyStructure (Alias S8 (LiteralStructure (Literal.To Word8 123)))
, PrettyStructure (Alias FlagStructure (LiteralStructure (Literal.To Literal.Bit 1)))
, PrettyStructure (LiteralStructure (Literal.Bits '[1,0,1,0]))
, PrettyStructure (Anonymous ("foo" :# U 64 'BE))
]
)
)