{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DoAndIfThenElse #-}
{-# LANGUAGE ExplicitNamespaces #-}
{-# LANGUAGE FlexibleInstances #-}
module SMR.Core.Codec.Poke
( type Poke
, pokeFileDecls
, pokeDecl
, pokeExp
, pokeRef)
where
import SMR.Core.Codec.Word
import SMR.Prim.Op.Base
import SMR.Core.Exp
import qualified Foreign.Marshal.Utils as F
import qualified Foreign.Marshal.Alloc as F
import qualified Foreign.Storable as F
import qualified Foreign.Ptr as F
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.ByteString.Unsafe as BS
import Data.Text (Text)
import Foreign.Ptr (Ptr)
import Control.Monad
import Data.Bits
import Data.Word
type Poke a = a -> Ptr Word8 -> IO (Ptr Word8)
pokeFileDecls :: Poke [Decl Text Prim]
pokeFileDecls ds
= pokeWord8 0x53
>=> pokeWord8 0x4d
>=> pokeWord8 0x52
>=> pokeWord8 0x31
>=> pokeList pokeDecl ds
{-# NOINLINE pokeFileDecls #-}
pokeDecl :: Poke (Decl Text Prim)
pokeDecl xx
= case xx of
DeclMac name x
-> pokeWord8 0xd0 >=> pokeText name >=> pokeExp x
DeclSet name x
-> pokeWord8 0xd1 >=> pokeText name >=> pokeExp x
{-# NOINLINE pokeDecl #-}
pokeExp :: Poke (Exp Text Prim)
pokeExp xx
= case xx of
XRef ref
-> pokeRef ref
XKey key x
-> pokeWord8 0xb1 >=> pokeKey key >=> pokeExp x
XApp x1 xs
| length xs <= 15
-> pokeApp (x1, xs)
| otherwise
-> pokeWord8 0xb2 >=> pokeExp x1 >=> pokeList pokeExp xs
XVar name i
| T.length name <= 15, i == 0
-> pokeVar name
| otherwise
-> pokeWord8 0xb3 >=> pokeName name >=> pokeBump i
XAbs ps x
| length ps <= 15
-> pokeAbs (ps, x)
| otherwise
-> pokeWord8 0xb4 >=> pokeList pokeParam ps >=> pokeExp x
XSub cs x
-> pokeWord8 0xb5 >=> pokeList pokeCar cs >=> pokeExp x
{-# NOINLINE pokeExp #-}
pokeAbs :: Poke ([Param], Exp Text Prim)
pokeAbs (ps, x)
= pokeWord8 (0x90 + fromIntegral (length ps))
>=> go ps
>=> pokeExp x
where go [] !p0 = return p0
go (p : ps1) !p0
= do p1 <- pokeParam p p0
go ps1 p1
{-# NOINLINE go #-}
{-# INLINE pokeAbs #-}
pokeApp :: Poke (Exp Text Prim, [Exp Text Prim])
pokeApp (x1, xs1)
= pokeWord8 (0xa0 + fromIntegral (length xs1))
>=> pokeExp x1
>=> go xs1
where go [] !p0 = return p0
go (x : xs) !p0
= do p1 <- pokeExp x p0
go xs p1
{-# NOINLINE go #-}
{-# INLINE pokeApp #-}
pokeKey :: Poke Key
pokeKey key
= case key of
KBox -> pokeWord8 0xb6
KRun -> pokeWord8 0xb7
{-# INLINE pokeKey #-}
pokeParam :: Poke Param
pokeParam pp
= case pp of
PParam tx PVal
-> pokeWord8 0xb8 >=> pokeName tx
PParam tx PExp
-> pokeWord8 0xb9 >=> pokeName tx
{-# INLINE pokeParam #-}
pokeCar :: Poke (Car Text Prim)
pokeCar car
= case car of
CSim (SSnv sbs)
-> pokeWord8 0xba >=> pokeList pokeSnvBind sbs
CRec (SSnv sbs)
-> pokeWord8 0xbb >=> pokeList pokeSnvBind sbs
CUps (UUps ups)
-> pokeWord8 0xbc >=> pokeList pokeUpsBump ups
{-# INLINE pokeCar #-}
pokeSnvBind :: Poke (SnvBind Text Prim)
pokeSnvBind !b
= case b of
BindVar n d x
-> pokeWord8 0xbd >=> pokeName n >=> pokeBump d >=> pokeExp x
BindNom n x
-> pokeWord8 0xbe >=> pokeNom n >=> pokeExp x
{-# INLINE pokeSnvBind #-}
pokeUpsBump :: Poke UpsBump
pokeUpsBump ((n, d), i)
= pokeWord8 0xbf >=> pokeName n >=> pokeBump d >=> pokeBump i
{-# INLINE pokeUpsBump #-}
pokeVar :: Poke Text
pokeVar !tx !p0
= do let bs = T.encodeUtf8 tx
BS.unsafeUseAsCStringLen bs $ \(pStr, nBytes)
-> if nBytes <= 15 then
do p1 <- pokeWord8 (0x80 + (fromIntegral nBytes)) p0
F.copyBytes (F.castPtr p1) pStr nBytes
return (F.plusPtr p1 nBytes)
else error "shimmer.pokeVar: var length too long"
{-# INLINE pokeVar #-}
pokeRef :: Poke (Ref Text Prim)
pokeRef !r
= case r of
RSym tx -> pokeName tx
RPrm p -> pokeWord8 0xc1 >=> pokePrim p
RTxt tx -> pokeWord8 0xc2 >=> pokeName tx
RMac tx -> pokeWord8 0xc3 >=> pokeName tx
RSet tx -> pokeWord8 0xc4 >=> pokeName tx
RNom i -> pokeWord8 0xc5 >=> pokeNom i
{-# INLINE pokeRef #-}
pokeName :: Poke Name
pokeName !p n
= pokeText p n
{-# INLINE pokeName #-}
pokeBump :: Poke Integer
pokeBump !n !p
= if n <= 2^(16 :: Int) then
do pokeWord16 (fromIntegral n) p
else error "shimmer.pokeBump: bump counter too large."
{-# NOINLINE pokeBump #-}
pokeNom :: Poke Integer
pokeNom !n !p
= if n <= 2^(28 :: Int) then
do pokeWord32 (fromIntegral n) p
else error "shimmer.pokeNom: nominal constant index too large."
{-# NOINLINE pokeNom #-}
pokePrim :: Poke Prim
pokePrim !pp
= case pp of
PrimTagUnit -> pokeWord8 0xe0
PrimTagList -> pokeWord8 0xe1
PrimLitBool True -> pokeWord8 0xe2
PrimLitBool False -> pokeWord8 0xe3
PrimLitWord8 w8 -> pokeWord8 0xe4 >=> pokeWord8 w8
PrimLitWord16 w16 -> pokeWord8 0xe5 >=> pokeWord16 w16
PrimLitWord32 w32 -> pokeWord8 0xe6 >=> pokeWord32 w32
PrimLitWord64 w64 -> pokeWord8 0xe7 >=> pokeWord64 w64
PrimLitInt8 i8 -> pokeWord8 0xe8 >=> pokeWord8 (fromIntegral i8)
PrimLitInt16 i16 -> pokeWord8 0xe9 >=> pokeWord16 (fromIntegral i16)
PrimLitInt32 i32 -> pokeWord8 0xea >=> pokeWord32 (fromIntegral i32)
PrimLitInt64 i64 -> pokeWord8 0xeb >=> pokeWord64 (fromIntegral i64)
PrimLitFloat32 f -> pokeWord8 0xec >=> pokeFloat32 f
PrimLitFloat64 f -> pokeWord8 0xed >=> pokeFloat64 f
PrimOp tx -> pokeWord8 0xee >=> pokeText tx
PrimLitNat n
-> pokeWord8 0xef
>=> pokeName (T.pack "nat")
>=> pokeList pokeWord8
[ fromIntegral $ (n .&. 0xff00000000000000) `shiftR` 56
, fromIntegral $ (n .&. 0x00ff000000000000) `shiftR` 48
, fromIntegral $ (n .&. 0x0000ff0000000000) `shiftR` 40
, fromIntegral $ (n .&. 0x000000ff00000000) `shiftR` 32
, fromIntegral $ (n .&. 0x00000000ff000000) `shiftR` 24
, fromIntegral $ (n .&. 0x0000000000ff0000) `shiftR` 16
, fromIntegral $ (n .&. 0x000000000000ff00) `shiftR` 8
, fromIntegral $ (n .&. 0x00000000000000ff)]
PrimLitInt n
-> pokeWord8 0xef
>=> pokeName (T.pack "int")
>=> pokeList pokeWord8
[ fromIntegral $ (n .&. 0xff00000000000000) `shiftR` 56
, fromIntegral $ (n .&. 0x00ff000000000000) `shiftR` 48
, fromIntegral $ (n .&. 0x0000ff0000000000) `shiftR` 40
, fromIntegral $ (n .&. 0x000000ff00000000) `shiftR` 32
, fromIntegral $ (n .&. 0x00000000ff000000) `shiftR` 24
, fromIntegral $ (n .&. 0x0000000000ff0000) `shiftR` 16
, fromIntegral $ (n .&. 0x000000000000ff00) `shiftR` 8
, fromIntegral $ (n .&. 0x00000000000000ff)]
{-# INLINE pokePrim #-}
pokeList :: Poke a -> Poke [a]
pokeList pokeA ls
= do let n = length ls
if n < 13 then
do pokeWord8 (0xf0 + (fromIntegral n)) >=> go ls
else if n <= 2^(8 :: Int) - 1
then pokeWord8 0xfd >=> pokeWord8 (fromIntegral n) >=> go ls
else if n <= 2^(16 :: Int) - 1
then pokeWord8 0xfe >=> pokeWord16 (fromIntegral n) >=> go ls
else if n <= 2^(28 :: Int)
then pokeWord8 0xff >=> pokeWord32 (fromIntegral n) >=> go ls
else error "shimmer.pokeList: list too long."
where go [] !p0 = return p0
go (x : xs) !p0
= do p1 <- pokeA x p0
go xs p1
{-# NOINLINE go #-}
{-# INLINE pokeList #-}
pokeText :: Poke Text
pokeText !tx !p0
= do let bs = T.encodeUtf8 tx
BS.unsafeUseAsCStringLen bs $ \(pStr, nBytes)
-> if nBytes < 13 then
do p1 <- pokeWord8 (0xf0 + (fromIntegral nBytes)) p0
F.copyBytes (F.castPtr p1) pStr nBytes
return (F.plusPtr p1 nBytes)
else if nBytes <= 255 then
do p1 <- pokeWord8 0xfd p0
p2 <- pokeWord8 (fromIntegral nBytes) p1
F.copyBytes (F.castPtr p2) pStr nBytes
return (F.plusPtr p2 nBytes)
else if nBytes <= 65535 then
do p1 <- pokeWord8 0xfe p0
p2 <- pokeWord16 (fromIntegral nBytes) p1
F.copyBytes (F.castPtr p2) pStr nBytes
return (F.plusPtr p2 nBytes)
else if nBytes <= 2^(28 :: Int) then
do p1 <- pokeWord8 0xff p0
p2 <- pokeWord32 (fromIntegral nBytes) p1
F.copyBytes (F.castPtr p2) pStr nBytes
return (F.plusPtr p2 nBytes)
else error "shimmer.pokeText: text string too large."
{-# NOINLINE pokeText #-}
pokeWord8 :: Poke Word8
pokeWord8 w p
= do F.poke p w
return (F.plusPtr p 1)
{-# INLINE pokeWord8 #-}
pokeWord16 :: Poke Word16
pokeWord16 w p
= do poke16 p 0 (toBE16 w)
return (F.plusPtr p 2)
{-# INLINE pokeWord16 #-}
pokeWord32 :: Poke Word32
pokeWord32 w p
= do poke32 p 0 (toBE32 w)
return (F.plusPtr p 4)
{-# INLINE pokeWord32 #-}
pokeWord64 :: Poke Word64
pokeWord64 w p
= do poke64 p 0 (toBE64 w)
return (F.plusPtr p 8)
{-# INLINE pokeWord64 #-}
pokeFloat32 :: Poke Float
pokeFloat32 f p
= F.allocaBytes 4 $ \p'
-> do F.poke (F.castPtr p' :: Ptr Float) f
w32 <- F.peek (F.castPtr p' :: Ptr Word32)
pokeWord32 w32 p
{-# INLINE pokeFloat32 #-}
pokeFloat64 :: Poke Double
pokeFloat64 f p
= F.allocaBytes 8 $ \p'
-> do F.poke (F.castPtr p' :: Ptr Double) f
w64 <- F.peek (F.castPtr p' :: Ptr Word64)
pokeWord64 w64 p
{-# INLINE pokeFloat64 #-}
from16 :: Word16 -> Word8
from16 = fromIntegral
{-# INLINE from16 #-}
from32 :: Word32 -> Word8
from32 = fromIntegral
{-# INLINE from32 #-}
from64 :: Word64 -> Word8
from64 = fromIntegral
{-# INLINE from64 #-}
poke8 :: Ptr a -> Int -> Word8 -> IO ()
poke8 p i w = F.pokeByteOff p i w
{-# INLINE poke8 #-}
poke16 :: Ptr a -> Int -> Word16 -> IO ()
poke16 p i w = F.pokeByteOff p i w
{-# INLINE poke16 #-}
poke32 :: Ptr a -> Int -> Word32 -> IO ()
poke32 p i w = F.pokeByteOff p i w
{-# INLINE poke32 #-}
poke64 :: Ptr a -> Int -> Word64 -> IO ()
poke64 p i w = F.pokeByteOff p i w
{-# INLINE poke64 #-}