{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}

module Ivory.Language.Ptr where

import Ivory.Language.IBool
import Ivory.Language.Area
import Ivory.Language.Proxy
import Ivory.Language.Ref
import Ivory.Language.Scope
import Ivory.Language.Type
import Ivory.Language.Monad
import qualified Ivory.Language.Syntax as I


-- Pointers --------------------------------------------------------------------

-- | Pointers (nullable references).
newtype Ptr (s :: RefScope) (a :: Area *) = Ptr { getPtr :: I.Expr }

instance IvoryArea area => IvoryType (Ptr s area) where
  ivoryType _ = I.TyPtr (ivoryArea (Proxy :: Proxy area))

instance IvoryArea area => IvoryVar (Ptr s area) where
  wrapVar    = wrapVarExpr
  unwrapExpr = getPtr

instance IvoryArea area => IvoryExpr (Ptr s area) where
  wrapExpr = Ptr

instance IvoryArea area => IvoryEq (Ptr s area)

-- Only allow global pointers to be stored in structures.
instance IvoryArea a => IvoryStore (Ptr Global a)

nullPtr :: IvoryArea area => Ptr s area
nullPtr  = Ptr (I.ExpLit I.LitNull)

-- | Convert a reference to a pointer.  This direction is safe as we know that
-- the reference is a non-null pointer.
refToPtr :: IvoryArea area => Ref s area -> Ptr s area
refToPtr  = wrapExpr . unwrapExpr

-- XXX do not export
ptrToRef :: IvoryArea area => Ptr s area -> Ref s area
ptrToRef  = wrapExpr . unwrapExpr

-- | Unwrap a pointer, and use it as a reference.
withRef :: IvoryArea area
        => Ptr as area
        -> (Ref as area -> Ivory eff t)
        -> Ivory eff f
        -> Ivory eff ()
withRef ptr t = ifte_ (nullPtr /=? ptr) (t (ptrToRef ptr))
