{- Generated by DrIFT (Automatic class derivations for Haskell) -}
{-# LINE 1 "src/FrontEnd/Tc/Kind.hs" #-}
module FrontEnd.Tc.Kind(
    Kind(..),
    KBase(..),
    Kindvar(..),
    KindConstraint(..),
    kindCombine,
    kindStar,
    kindUTuple,
    kindFunRet,
    kindHash,
    kindArg,
    isSubsumedBy,
    unfoldKind
    ) where

import Control.Monad
import Data.IORef
import Data.Monoid

import Data.Binary
import Doc.DocLike
import Doc.PPrint(pprint,pprintPrec,pprintAssoc,Assoc(..),PPrint,pprintBinary)
import Name.Name

{-

 KQuest = ?        star or hash or unboxed tuple
 KQuestQuest = ??  star or hash
 KUTuple = (#)     unboxed tuple
 Star    = *       boxed value
 KHash   = #       unboxed value
 Kfun    = (->)
 KNamed Foo = Foo  named kind

 we have the following subkinding going on

       ?
      / \
     ?? (#)
     /\
    *  #

in addition, user defined named kinds are allowed. these can only occur via
kind annotations, and only unify with themselves

-}

data KBase =
        Star
        | KHash
        | KUTuple
        | KQuestQuest
        | KQuest
        | KNamed Name
    deriving(Eq, Ord)   -- but we need them for kind inference
    {-! derive: Binary !-}

KNamed s1 `isSubsumedBy2` KNamed s2   = s1 == s2
_         `isSubsumedBy2` KQuest      = True
Star      `isSubsumedBy2` KQuestQuest = True
KHash     `isSubsumedBy2` KQuestQuest = True
k1        `isSubsumedBy2` k2          = k1 == k2

kindStar   = KBase Star
kindHash   = KBase KHash
kindUTuple = KBase KUTuple
kindFunRet = KBase KQuest
kindArg    = KBase KQuestQuest

infixr 5 `Kfun`

data Kind  = KBase KBase
           | Kfun Kind Kind
           | KVar Kindvar               -- variables aren't really allowed in haskell in kinds
             deriving(Eq, Ord)   -- but we need them for kind inference
    {-! derive: Binary !-}

KBase kb    `isSubsumedBy` KBase kb'    = isSubsumedBy2 kb kb'
Kfun  k1 k2 `isSubsumedBy` Kfun k1' k2' = isSubsumedBy k1 k1' && isSubsumedBy k2 k2'
_           `isSubsumedBy` _            = False

kindCombine :: Monad m => Kind -> Kind -> m Kind
kindCombine x y = g x y where
    f x y | x == y = return x

    f KQuest x = fquest x
    f x  KQuest = fquest x
    f KQuestQuest x = fquest2 x
    f x  KQuestQuest = fquest2 x
    f x y = fail $ "kindCombine: " ++ show (x,y)
    fquest (KNamed n) = fail $ "Attempt to unify named kind" <+> tshow n <+> "with ?"
    fquest x = return x
    fquest2 (KNamed n) = fail $ "Attempt to unify named kind" <+> tshow n <+> "with ??"
    fquest2 KUTuple = fail $ "Attempt to unify unboxed tuple with ??"
    fquest2 KQuest = return KQuestQuest
    fquest2 x = return x
    g (KBase x) (KBase y) = f x y >>= return . KBase
    g (Kfun a b) (Kfun a' b') = return Kfun `ap` g a a' `ap` g b b'
    g x y = fail $ "kindCombine: " ++ show (x,y)

data KindConstraint
    = KindSimple     -- ^ * | kindSimple -> kindSimple
    | KindQuest      -- ^ ?, so * or (#) or #
    | KindQuestQuest -- ^ ??, * or #
    | KindStar       -- ^ must be *
    | KindAny        -- ^ may be anything
    deriving(Eq,Ord,Show)

-- note that named kinds are never infered, so we don't need constraints
-- mentioning them.

instance Monoid KindConstraint where
    mempty = KindAny
    mappend a b | a == b = a
    mappend KindAny k = k
    mappend KindStar _ = KindStar
    mappend KindSimple KindQuest = KindStar
    mappend KindSimple KindQuestQuest = KindStar
    mappend KindQuest KindQuestQuest = KindQuestQuest
    mappend k1 k2 = mappend k2 k1

data Kindvar = Kindvar {
    kvarUniq       :: {-# UNPACK #-} !Int,
    kvarRef        :: {-# UNPACK #-} !(IORef (Maybe Kind)),
    kvarConstraint :: !KindConstraint
    }

instance Binary Kindvar where
    put _ = return ()
    get = return (error "Binary.Kindvar.get")

instance Eq Kindvar where
    a == b = kvarUniq a == kvarUniq b

instance Ord Kindvar where
    a `compare` b = kvarUniq a `compare` kvarUniq b

instance Show Kind where
    showsPrec n k = pprintPrec n k

instance Show Kindvar where
    showsPrec n k = pprintPrec n k

instance Show KBase where
    showsPrec _ Star    = showString "*"
    showsPrec _ KUTuple = showString "(#)"
    showsPrec _ KHash   = showString "#"
    showsPrec _ KQuest  = showString "?"
    showsPrec _ KQuestQuest = showString "??"
    showsPrec _ (KNamed n) = shows n

instance DocLike d => PPrint d KBase where
    pprint kb = text (show kb)

instance DocLike d => PPrint d Kind where
   pprintAssoc _ _ (KBase b) = pprint b
   pprintAssoc _ _ (KVar kindVar)   = pprint kindVar
   pprintAssoc a n (Kfun k1 k2) = pprintBinary AssocRight 5 a n k1 (text "->") k2 -- checkAssoc AssocRight 5 a n $ pprintPrec 5 k1 <+> text "->" <+> pprintAssoc AssocRight 5 k2

instance DocLike d =>  PPrint d Kindvar where
   pprint Kindvar { kvarUniq = s } = text $ 'k':show s

--  * -> * == [*,*]
--  (*->*->*) -> * -> * == [(*->*->*), *, *]
unfoldKind :: Kind -> [Kind]
unfoldKind (Kfun k1 k2) = k1 : unfoldKind k2
unfoldKind v = [v]
{-* Generated by DrIFT : Look, but Don't Touch. *-}
instance Data.Binary.Binary KBase where
    put Star = do
	    Data.Binary.putWord8 0
    put KHash = do
	    Data.Binary.putWord8 1
    put KUTuple = do
	    Data.Binary.putWord8 2
    put KQuestQuest = do
	    Data.Binary.putWord8 3
    put KQuest = do
	    Data.Binary.putWord8 4
    put (KNamed aa) = do
	    Data.Binary.putWord8 5
	    Data.Binary.put aa
    get = do
	    h <- Data.Binary.getWord8
	    case h of
	      0 -> do
		    return Star
	      1 -> do
		    return KHash
	      2 -> do
		    return KUTuple
	      3 -> do
		    return KQuestQuest
	      4 -> do
		    return KQuest
	      5 -> do
		    aa <- Data.Binary.get
		    return (KNamed aa)
	      _ -> fail "invalid binary data found"

instance Data.Binary.Binary Kind where
    put (KBase aa) = do
	    Data.Binary.putWord8 0
	    Data.Binary.put aa
    put (Kfun ab ac) = do
	    Data.Binary.putWord8 1
	    Data.Binary.put ab
	    Data.Binary.put ac
    put (KVar ad) = do
	    Data.Binary.putWord8 2
	    Data.Binary.put ad
    get = do
	    h <- Data.Binary.getWord8
	    case h of
	      0 -> do
		    aa <- Data.Binary.get
		    return (KBase aa)
	      1 -> do
		    ab <- Data.Binary.get
		    ac <- Data.Binary.get
		    return (Kfun ab ac)
	      2 -> do
		    ad <- Data.Binary.get
		    return (KVar ad)
	      _ -> fail "invalid binary data found"

--  Imported from other files :-