{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ExplicitNamespaces #-}
{-# LANGUAGE FlexibleInstances #-}
module SMR.Core.Codec.Peek
( type Peek
, peekFileDecls
, peekDecl
, peekExp
, peekRef)
where
import SMR.Prim.Op.Base
import SMR.Core.Codec.Word
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 Control.Monad
import Foreign.Ptr
import Data.Text (Text)
import Data.Bits
import Data.Word
import Data.Int
import Numeric
type Peek a = Ptr Word8 -> Int -> IO (a, Ptr Word8, Int)
peekFileDecls :: Peek [Decl Text Prim]
peekFileDecls !p0 !n0
= do (b0, p1, n1) <- peekWord8 p0 n0
(b1, p2, n2) <- peekWord8 p1 n1
(b2, p3, n3) <- peekWord8 p2 n2
(b3, p4, n4) <- peekWord8 p3 n3
when ( b0 /= 0x53 || b1 /= 0x4d || b2 /= 0x52 || b3 /= 0x31)
$ error "shimmer.peekFileDecls: bad magic"
(ds, p5, n5) <- peekList peekDecl p4 n4
return (ds, p5, n5)
{-# NOINLINE peekFileDecls #-}
peekDecl :: Peek (Decl Text Prim)
peekDecl !p0 !n0
= do (b0, p1, n1) <- peekWord8 p0 n0
p1 `seq` case b0 of
0xd0
-> do (tx, p2, n2) <- peekName p1 n1
(x, p3, n3) <- peekExp p2 n2
return (DeclMac tx x, p3, n3)
0xd1
-> do (tx, p2, n2) <- peekName p1 n1
(x, p3, n3) <- peekExp p2 n2
return (DeclSet tx x, p3, n3)
_ -> error $ failHeaderByte "peekDecl" b0 n0
{-# NOINLINE peekDecl #-}
peekExp :: Peek (Exp Text Prim)
peekExp !p0 !n0
= do (b0, p1, n1) <- peekWord8 p0 n0
p1 `seq` case b0 of
0xb0
-> do (r, p2, n2) <- peekRef p1 n1
return (XRef r, p2, n2)
0xb1
-> do (key, p2, n2) <- peekKey p1 n1
(xx, p3, n3) <- peekExp p2 n2
return (XKey key xx, p3, n3)
0xb2
-> do (x1, p2, n2) <- peekExp p1 n1
(xs, p3, n3) <- peekList peekExp p2 n2
return (XApp x1 xs, p3, n3)
0xb3
-> do (n, p2, n2) <- peekName p1 n1
(i, p3, n3) <- peekBump p2 n2
return (XVar n i, p3, n3)
0xb4
-> do (ps, p2, n2) <- peekList peekParam p1 n1
(x, p3, n3) <- peekExp p2 n2
return (XAbs ps x, p3, n3)
0xb5
-> do (cs, p2, n2) <- peekList peekCar p1 n1
(x, p3, n3) <- peekExp p2 n2
return (XSub cs x, p3, n3)
_ -> case b0 .&. 0x0f0 of
0x80
-> do (tx, p2, n2) <- peekVar p0 n0
return (XVar tx 0, p2, n2)
0x90 -> peekAbs p0 n0
0xa0 -> peekApp p0 n0
0xc0
-> do (r, p2, n2) <- peekRef p0 n0
return (XRef r, p2, n2)
0xf0
-> do (tx, p2, n2) <- peekText p0 n0
return (XRef $ RSym tx, p2, n2)
_ -> failHeaderByte "peekExp" b0 n0
{-# NOINLINE peekExp #-}
peekAbs :: Peek (Exp Text Prim)
peekAbs p0 n0
| n0 >= 1
= do (b0, p1, n1) <- peekWord8' p0 n0
when ((b0 .&. 0x0f0) /= 0x90)
$ failHeaderByte "peekAbs" b0 n0
go (fromIntegral $ b0 .&. 0x00f) [] p1 n1
| otherwise
= error "shimmer.peekAbs: short header"
where go (0 :: Int) acc p n
= do (x, p2, n2) <- peekExp p n
return (XAbs (reverse acc) x, p2, n2)
go i acc p n
= do (x, p', n') <- peekParam p n
go (i - 1) (x : acc) p' n'
{-# NOINLINE go #-}
{-# INLINE peekAbs #-}
peekApp :: Peek (Exp Text Prim)
peekApp p0 n0
| n0 >= 1
= do (b0, p1, n1) <- peekWord8' p0 n0
when ((b0 .&. 0x0f0) /= 0xa0)
$ failHeaderByte "peekApp" b0 n0
(x0, p2, n2) <- peekExp p1 n1
go x0 (fromIntegral $ b0 .&. 0x00f) [] p2 n2
| otherwise
= error "shimmer.peekApp: short header"
where go x0 (0 :: Int) acc p n
= do return (XApp x0 (reverse acc), p, n)
go x0 i acc p n
= do (x, p', n') <- peekExp p n
go x0 (i - 1) (x : acc) p' n'
{-# NOINLINE go #-}
{-# INLINE peekApp #-}
peekKey :: Peek Key
peekKey !p0 !n0
= do (b0, p1, n1) <- peekWord8 p0 n0
p1 `seq` case b0 of
0xb6 -> return (KBox, p1, n1)
0xb7 -> return (KRun, p1, n1)
_ -> failHeaderByte "peekKey" b0 n0
{-# INLINE peekKey #-}
peekParam :: Peek Param
peekParam !p0 !n0
= do (b0, p1, n1) <- peekWord8 p0 n0
p1 `seq` case b0 of
0xb8
-> do (tx, p2, n2) <- peekName p1 n1
return (PParam tx PVal, p2, n2)
0xb9
-> do (tx, p2, n2) <- peekName p1 n1
return (PParam tx PExp, p2, n2)
_ -> failHeaderByte "peekParam" b0 n0
{-# INLINE peekParam #-}
peekCar :: Peek (Car Text Prim)
peekCar !p0 !n0
= do (b0, p1, n1) <- peekWord8 p0 n0
p1 `seq` case b0 of
0xba
-> do (sbs, p2, n2) <- peekList peekSnvBind p1 n1
return (CSim (SSnv sbs), p2, n2)
0xbb
-> do (sbs, p2, n2) <- peekList peekSnvBind p1 n1
return (CRec (SSnv sbs), p2, n2)
0xbc
-> do (ups, p2, n2) <- peekList peekUpsBump p1 n1
return (CUps (UUps ups), p2, n2)
_ -> failHeaderByte "peekCar" b0 n1
{-# INLINE peekCar #-}
peekSnvBind :: Peek (SnvBind Text Prim)
peekSnvBind !p0 !n0
= do (b0, p1, n1) <- peekWord8 p0 n0
p1 `seq` case b0 of
0xbd
-> do (n, p2, n2) <- peekName p1 n1
(d, p3, n3) <- peekBump p2 n2
(x, p4, n4) <- peekExp p3 n3
return (BindVar n d x, p4, n4)
0xbe
-> do (n, p2, n2) <- peekNom p1 n1
(x, p3, n3) <- peekExp p2 n2
return (BindNom n x, p3, n3)
_ -> failHeaderByte "peekSnvBind" b0 n1
{-# INLINE peekSnvBind #-}
peekUpsBump :: Peek UpsBump
peekUpsBump !p0 !n0
= do (b0, p1, n1) <- peekWord8 p0 n0
when (b0 /= 0xbf)
$ failHeaderByte "peekUpsBump" b0 n1
(n, p2, n2) <- peekName p1 n1
(d, p3, n3) <- peekBump p2 n2
(i, p4, n4) <- peekBump p3 n3
return $ (((n, d), i), p4, n4)
{-# INLINE peekUpsBump #-}
peekRef :: Peek (Ref Text Prim)
peekRef !p0 !n0
= do (b0, p1, n1) <- peekWord8 p0 n0
p1 `seq` case b0 of
0xc0
-> do (tx, p2, n2) <- peekText p1 n1
return (RSym tx, p2, n2)
0xc1
-> do (m, p2, n2) <- peekPrim p1 n1
return (RPrm m, p2, n2)
0xc2
-> do (tx, p2, n2) <- peekText p1 n1
return (RTxt tx, p2, n2)
0xc3
-> do (tx, p2, n2) <- peekText p1 n1
return (RMac tx, p2, n2)
0xc4
-> do (tx, p2, n2) <- peekText p1 n1
return (RSet tx, p2, n2)
0xc5
-> do (i, p2, n2) <- peekNom p1 n1
return (RNom i, p2, n2)
_
-> do (r, p1', n1') <- peekName p0 n0
return (RSym r, p1', n1')
{-# INLINE peekRef #-}
peekName :: Peek Name
peekName !p !n
= do peekText p n
{-# INLINE peekName #-}
peekBump :: Peek Integer
peekBump !p0 !n0
= do (i, p1, n1) <- peekWord16 p0 n0
return (fromIntegral i, p1, n1)
{-# INLINE peekBump #-}
peekNom :: Peek Integer
peekNom !p0 !n0
= do (i, p1, n1) <- peekWord32 p0 n0
return (fromIntegral i, p1, n1)
{-# INLINE peekNom #-}
peekPrim :: Peek Prim
peekPrim !p0 !n0
| n0 >= 1
= do (b0, p1, n1) <- peekWord8' p0 n0
p1 `seq` case b0 of
0xe0 -> return (PrimTagUnit, p1, n1)
0xe1 -> return (PrimTagList, p1, n1)
0xe2 -> return (PrimLitBool True, p1, n1)
0xe3 -> return (PrimLitBool False, p1, n1)
0xe4
-> do (w8, p2, n2) <- peekWord8 p1 n1
return (PrimLitWord8 w8, p2, n2)
0xe5
-> do (w16, p2, n2) <- peekWord16 p1 n1
return (PrimLitWord16 w16, p2, n2)
0xe6
-> do (w32, p2, n2) <- peekWord32 p1 n1
return (PrimLitWord32 w32, p2, n2)
0xe7
-> do (w64, p2, n2) <- peekWord64 p1 n1
return (PrimLitWord64 w64, p2, n2)
0xe8
-> do (w8, p2, n2) <- peekWord8 p1 n1
return (PrimLitInt8 $ fromIntegral w8, p2, n2)
0xe9
-> do (w16, p2, n2) <- peekWord16 p1 n1
return (PrimLitInt16 $ fromIntegral w16, p2, n2)
0xea
-> do (w32, p2, n2) <- peekWord32 p1 n1
return (PrimLitInt32 $ fromIntegral w32, p2, n2)
0xeb
-> do (w64, p2, n2) <- peekWord64 p1 n1
return (PrimLitInt64 $ fromIntegral w64, p2, n2)
0xec
-> do (f32, p2, n2) <- peekFloat32 p1 n1
return (PrimLitFloat32 f32, p2, n2)
0xed
-> do (f64, p2, n2) <- peekFloat64 p1 n1
return (PrimLitFloat64 f64, p2, n2)
0xee
-> do (tx, p2, n2) <- peekText p1 n1
return (PrimOp tx, p2, n2)
0xef
-> do (tx, p2, n2) <- peekText p1 n1
case T.unpack tx of
"nat"
-> do (ls, p3, n3) <- peekList peekWord8 p2 n2
case ls of
[x0, x1, x2, x3, x4, x5, x6, x7]
-> do let w = to64 x0 `shiftL` 56
.|. to64 x1 `shiftL` 48
.|. to64 x2 `shiftL` 40
.|. to64 x3 `shiftL` 32
.|. to64 x4 `shiftL` 24
.|. to64 x5 `shiftL` 16
.|. to64 x6 `shiftL` 8
.|. to64 x7
return (PrimLitNat $ fromIntegral w, p3, n3)
"int"
-> do (ls, p3, n3) <- peekList peekWord8 p2 n2
case ls of
[x0, x1, x2, x3, x4, x5, x6, x7]
-> do let w = to64 x0 `shiftL` 56
.|. to64 x1 `shiftL` 48
.|. to64 x2 `shiftL` 40
.|. to64 x3 `shiftL` 32
.|. to64 x4 `shiftL` 24
.|. to64 x5 `shiftL` 16
.|. to64 x6 `shiftL` 8
.|. to64 x7
F.allocaBytes 8 $ \pp
-> do F.poke (F.castPtr pp :: Ptr Word64) w
i64 <- F.peek (F.castPtr pp :: Ptr Int64)
return (PrimLitInt (fromIntegral i64), p3, n3)
_ -> error "shimmer.peekPrim: invalid payload"
s -> error $ "shimmer.peekPrim: unknown tag " ++ show s
_ -> failHeaderByte "peekPrim" b0 n1
| otherwise
= error "shimmer.peekPrim: short header"
{-# INLINE peekPrim #-}
peekList :: Peek a -> Peek [a]
peekList peekA p0 n0
| n0 >= 1
= do (b0, p1, n1) <- peekWord8' p0 n0
case b0 of
0xfd
| n1 >= 1
-> do nElems <- fmap fromIntegral $ peek8 p0 1
go nElems [] (F.plusPtr p0 2) (n1 - 1)
0xfe
| n1 >= 2
-> do nElems <- fmap fromIntegral $ peek16 p0 1
go nElems [] (F.plusPtr p0 3) (n1 - 2)
0xff
| n1 >= 4
-> do nElems <- fmap fromIntegral $ peek32 p0 1
go nElems [] (F.plusPtr p0 5) (n1 - 4)
_ | (b0 .&. 0x0f0) == 0xf0
-> let nElems = fromIntegral (b0 .&. 0x0f)
in go nElems [] p1 n1
| otherwise
-> failHeaderByte "peekList" b0 n0
| otherwise
= error "shimmer.peekList: short header"
where go (0 :: Int) acc p n
= return (reverse acc, p, n)
go i acc p n
= do (x, p', n') <- peekA p n
go (i - 1) (x : acc) p' n'
{-# NOINLINE go #-}
{-# INLINE peekList #-}
peekVar :: Peek Text
peekVar !p0 !n0
| n0 >= 1
= do (b0, p1, n1) <- peekWord8' p0 n0
when ((b0 .&. 0x0f0) /= 0x80)
$ failHeaderByte "peekVar" b0 n0
let nBytes = fromIntegral $ b0 .&. 0x0f
buf <- F.mallocBytes nBytes
F.copyBytes buf (F.castPtr p1) nBytes
bs <- BS.unsafePackMallocCStringLen (buf, nBytes)
return (T.decodeUtf8 bs, F.plusPtr p1 nBytes, n1 - nBytes)
| otherwise
= error "shimmer.peekVar: short header"
peekText :: Peek Text
peekText !p0 !n0
| n0 >= 1
= do (b0, p1, n1) <- peekWord8' p0 n0
case b0 of
0xfd
| n1 >= 1
-> do nBytes <- fmap fromIntegral $ peek8 p0 1
buf <- F.mallocBytes nBytes
let p2 = F.plusPtr p0 2
let n2 = n0 - 2
when (not (n2 >= nBytes))
$ error $ "shimmer.peekText.fd: pointer out of range"
F.copyBytes buf p2 nBytes
bs <- BS.unsafePackMallocCStringLen (buf, nBytes)
return (T.decodeUtf8 bs, F.plusPtr p2 nBytes, n2 - nBytes)
0xfe
| n1 >= 2
-> do nBytes <- fmap fromIntegral $ peek16 p0 1
buf <- F.mallocBytes nBytes
let p2 = F.plusPtr p0 3
let n2 = n0 - 3
when (not (n2 >= nBytes))
$ error "shimmer.peekText.fe: pointer out of range"
F.copyBytes buf p2 nBytes
bs <- BS.unsafePackMallocCStringLen (buf, nBytes)
return (T.decodeUtf8 bs, F.plusPtr p2 nBytes, n2 - nBytes)
0xff
| n1 >= 4
-> do nBytes <- fmap fromIntegral $ peek32 p0 1
buf <- F.mallocBytes nBytes
let p2 = F.plusPtr p0 5
let n2 = n0 - 5
when (not (n2 >= nBytes))
$ error "shimmer.peekText.ff: pointer out of range"
F.copyBytes buf p2 nBytes
bs <- BS.unsafePackMallocCStringLen (buf, nBytes)
return (T.decodeUtf8 bs, F.plusPtr p2 nBytes, n2 - nBytes)
_
-> do when ((b0 .&. 0x0f0) /= 0xf0)
$ error $ "shimmer.peekVar.fN: invalid header " ++ show b0
let nBytes = fromIntegral $ b0 .&. 0x0f
buf <- F.mallocBytes nBytes
F.copyBytes buf (F.castPtr p1) nBytes
bs <- BS.unsafePackMallocCStringLen (buf, nBytes)
return (T.decodeUtf8 bs, F.plusPtr p1 nBytes, n1 - nBytes)
| otherwise
= error "shimmer.peekText.start: pointer out of range"
{-# NOINLINE peekText #-}
peekWord8 :: Peek Word8
peekWord8 p n
| n >= 1 = peekWord8' p n
| otherwise = error "shimmer.peekWord8: pointer out of bounds"
{-# NOINLINE peekWord8 #-}
peekWord8' :: Peek Word8
peekWord8' p n
= do w <- F.peek p
return (w, F.plusPtr p 1, n - 1)
{-# INLINE peekWord8' #-}
peekWord16 :: Peek Word16
peekWord16 p n
| n >= 2 = peekWord16' p n
| otherwise = error "shimmer.peekWord16: pointer out of bounds"
{-# NOINLINE peekWord16 #-}
peekWord16' :: Peek Word16
peekWord16' p n
= do w <- fmap fromBE16 $ peek16 p 0
return (w, F.plusPtr p 2, n - 2)
{-# INLINE peekWord16' #-}
peekWord32 :: Peek Word32
peekWord32 p n
| n >= 4 = peekWord32' p n
| otherwise = error "shimmer.peekWord32: pointer out of bounds"
{-# NOINLINE peekWord32 #-}
peekWord32' :: Peek Word32
peekWord32' p n
= do w <- fmap fromBE32 $ peek32 p 0
return (w, F.plusPtr p 4, n - 4)
{-# INLINE peekWord32' #-}
peekWord64 :: Peek Word64
peekWord64 p n
| n >= 8 = peekWord64' p n
| otherwise = error "shimmer.peekWord64: pointer out of bounds"
{-# NOINLINE peekWord64 #-}
peekWord64' :: Peek Word64
peekWord64' p n
= do w <- fmap fromBE64 $ peek64 p 0
return (w, F.plusPtr p 8, n - 8)
{-# INLINE peekWord64' #-}
peekFloat32 :: Peek Float
peekFloat32 p0 n0
| n0 >= 4
= F.allocaBytes 4 $ \p'
-> do (w32, p1, n1) <- peekWord32' p0 n0
F.poke (F.castPtr p' :: Ptr Word32) w32
f32 <- F.peek (F.castPtr p' :: Ptr Float)
return (f32, p1, n1)
| otherwise = error "shimmer.peekFloat32: pointer out of bounds"
{-# NOINLINE peekFloat32 #-}
peekFloat64 :: Peek Double
peekFloat64 p0 n0
| n0 >= 8
= F.allocaBytes 8 $ \p'
-> do (w64, p1, n1) <- peekWord64' p0 n0
F.poke (F.castPtr p' :: Ptr Word64) w64
f64 <- F.peek (F.castPtr p' :: Ptr Double)
return (f64, p1, n1)
| otherwise = error "shimmer.peekFloat64: pointer out of bounds"
{-# NOINLINE peekFloat64 #-}
to16 :: Word8 -> Word16
to16 = fromIntegral
{-# INLINE to16 #-}
to64 :: Word8 -> Word64
to64 = fromIntegral
{-# INLINE to64 #-}
to32 :: Word8 -> Word32
to32 = fromIntegral
{-# INLINE to32 #-}
peek8 :: Ptr a -> Int -> IO Word8
peek8 p o = F.peekByteOff p o
{-# INLINE peek8 #-}
peek16 :: Ptr a -> Int -> IO Word16
peek16 p o = F.peekByteOff p o
{-# INLINE peek16 #-}
peek32 :: Ptr a -> Int -> IO Word32
peek32 p o = F.peekByteOff p o
{-# INLINE peek32 #-}
peek64 :: Ptr a -> Int -> IO Word64
peek64 p o = F.peekByteOff p o
{-# INLINE peek64 #-}
failHeaderByte :: String -> Word8 -> Int -> a
failHeaderByte fn b n
= error
$ "shimmer." ++ fn
++ " invalid header byte "
++ showHex b "" ++ "@-" ++ showHex n ""