module Ermine.Syntax.Global
(
Global(Global)
, AsGlobal(..)
, HasGlobal(..)
, Assoc(..)
, Fixity(..)
, _Fixity
, unpackFixity
, packFixity
, glob
) where
import Control.Applicative
import Control.Lens
import Control.Monad
import Crypto.Hash.MD5 as MD5
import Data.Binary (Binary)
import qualified Data.Binary as Binary
import Data.Bits
import Data.Bytes.Get
import Data.Bytes.Put
import Data.Bytes.Serial
import Data.ByteString
import Data.Data (Data, Typeable)
import Data.Function (on)
import Data.Hashable
import Data.Serialize (Serialize)
import qualified Data.Serialize as Serialize
import Data.Text
import Data.Word
import Ermine.Syntax.Digest
import Ermine.Syntax.ModuleName
import Ermine.Syntax.Name
import GHC.Generics (Generic)
data Assoc = L | R | N deriving (Eq,Ord,Show,Read,Enum,Data,Typeable,Generic)
instance Digestable Assoc
data Fixity
= Infix !Assoc !Int
| Prefix !Int
| Postfix !Int
| Idfix
deriving (Eq,Ord,Show,Read,Data,Typeable,Generic)
packFixity :: Fixity -> Word8
packFixity Idfix = 0xC0
packFixity (Prefix n) = 0x40 .|. (0x0F .&. fromIntegral n)
packFixity (Postfix n) = 0x80 .|. (0x0F .&. fromIntegral n)
packFixity (Infix a n) = packAssoc a .|. (0x0F .&. fromIntegral n)
where
packAssoc L = 0x00
packAssoc R = 0x10
packAssoc N = 0x20
unpackFixity :: Monad m => Word8 -> m Fixity
unpackFixity w8 =
case 0xC0 .&. w8 of
0x00 -> case 0x30 .&. w8 of
0x00 -> return $ Infix L n
0x10 -> return $ Infix R n
0x20 -> return $ Infix N n
_ -> fail "unpackFixity: bad associativity"
0x40 -> return $ Prefix n
0x80 -> return $ Postfix n
0xC0 -> return Idfix
_ -> fail "unpackFixity: IMPOSSIBLE"
where n = fromIntegral $ 0x0F .&. w8
_Fixity :: Prism' Word8 Fixity
_Fixity = prism' packFixity unpackFixity
instance Serial Fixity where
serialize f = putWord8 $ packFixity f
deserialize = do
w <- getWord8
unpackFixity w
instance Binary Fixity where
put = serialize
get = deserialize
instance Serialize Fixity where
put = serialize
get = deserialize
instance Digestable Fixity
data Global = Global
{ _globalDigest :: !ByteString
, _globalFixity :: !Fixity
, _globalModule :: !ModuleName
, _globalName :: !Text
} deriving (Data, Typeable, Generic)
instance Show Global where
showsPrec d (Global _ f m n) = showParen (d > 10) $
showString "glob " . showsPrec 11 f .
showChar ' ' . showsPrec 11 m .
showChar ' ' . showsPrec 11 n
instance Read Global where
readsPrec d = readParen (d > 10) $ \r -> do
("glob", r') <- lex r
(f, r'') <- readsPrec 11 r'
(m, r''') <- readsPrec 11 r''
(n, r'''') <- readsPrec 11 r'''
return (glob f m n, r'''')
instance Serial Global where
serialize (Global d f m n) = serialize d >> serialize f >> serialize m >> serialize n
deserialize = liftM4 Global deserialize deserialize deserialize deserialize
instance Binary Global where
put = serialize
get = deserialize
instance HasModuleName Global where
module_ g (Global _ f m n) = g m <&> \m' -> glob f m' n
instance HasName Global where
name g (Global _ f m n) = g n <&> \n' -> glob f m n'
class AsGlobal t where
_Global :: Prism' t Global
instance AsGlobal Global where
_Global = id
class HasGlobal t where
global :: Lens' t Global
fixity :: Lens' t Fixity
fixity f = global $ \ (Global _ a m n) -> (\a' -> glob a' m n) <$> f a
instance HasGlobal Global where
global = id
instance Eq Global where
(==) = (==) `on` _globalDigest
instance Ord Global where
compare = compare `on` _globalDigest
instance Digestable Global where
digest c = digest c . _globalDigest
instance Hashable Global where
hashWithSalt s c = hashWithSalt s (_globalDigest c)
glob :: AsGlobal t => Fixity -> ModuleName -> Text -> t
glob f m n = _Global # Global d f m n where
d = MD5.finalize $ digest MD5.init f `digest` m `digest` n